Esempio n. 1
0
void SetEncodedRGBValue(Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, unsigned int max, unsigned int red, unsigned int green, unsigned int blue)
{
	if (!img->IsIndexed() && img->GetMaxIntValue() == max && GammaCurve::IsNeutral(g))
		// avoid potential re-quantization in case we have a pretty match between encoded data and container
		img->SetRGBValue(x, y, red, green, blue);
	else
		img->SetRGBValue(x, y, IntDecode(g,red,max), IntDecode(g,green,max), IntDecode(g,blue,max));
}
Esempio n. 2
0
float* GammaCurve::GetLookupTable(unsigned int max)
{
    POV_COLOURSPACE_ASSERT(max == 255 || max == 65535); // shouldn't happen, but it won't hurt to check in debug versions

    // Get a reference to the lookup table pointer we're dealing with, so we don't need to duplicate all the remaining code.
    float*& lookupTable = (max == 255 ? lookupTable8 : lookupTable16);

#if POV_MULTITHREADED
    // Make sure we're not racing any other thread that might currently be busy creating the LUT.
    boost::mutex::scoped_lock lock(lutMutex);
#endif

    // Create the LUT if it doesn't exist yet.
    if (!lookupTable)
    {
        float* tempTable = new float[max+1];
        for (unsigned int i = 0; i <= max; i ++)
            tempTable[i] = Decode(IntDecode(i, max));

        // hook up the table only as soon as it is completed, so that querying the table does not need to
        // care about thread-safety.
        lookupTable = tempTable;
    }

    return lookupTable;
}
Esempio n. 3
0
void SetEncodedGrayAValue(Image* img, unsigned int x, unsigned int y, const GammaCurvePtr& g, unsigned int max, unsigned int gray, unsigned int alpha, bool premul)
{
	bool doPremultiply   = (alpha != max) && !premul && (img->IsPremultiplied() || !img->HasTransparency()); // need to apply premultiplication if encoded data isn't PM'ed but container content should be
	bool doUnPremultiply = (alpha != max) && premul && !img->IsPremultiplied() && img->HasTransparency(); // need to undo premultiplication if other way round
	if (!doPremultiply && !doUnPremultiply && !img->IsIndexed() && img->GetMaxIntValue() == max && GammaCurve::IsNeutral(g))
		// avoid potential re-quantization in case we have a pretty match between encoded data and container
		img->SetGrayAValue(x, y, gray, alpha);
	else
	{
		float fAlpha = IntDecode(alpha,max);
		float fGray  = IntDecode(g,gray,max);
		if (doPremultiply)
			AlphaPremultiply(fGray, fAlpha);
		else if (doUnPremultiply)
			AlphaUnPremultiply(fGray, fAlpha);
		// else no need to worry about premultiplication
		img->SetGrayAValue(x, y, fGray, fAlpha);
	}
}
Esempio n. 4
0
Image *Read (IStream *file, const Image::ReadOptions& options)
{
    int                   nrow;
    int                   result = 0;
    long                  LineSize;
    TIFF                  *tif;
    Image                 *image ;
    uint16                BitsPerSample;
    uint16                BytesPerSample = 1;
    uint16                PhotometricInterpretation;
    uint16                SamplePerPixel;
    uint16                Orientation;
    uint32                RowsPerStrip;
    unsigned int          width;
    unsigned int          height;

    // TODO - TIFF files probably have some gamma info in them by default, but we're currently ignorant about that.
    // Until that is fixed, use whatever the user has chosen as default.
    GammaCurvePtr gamma;
    if (options.gammacorrect && options.defaultGamma)
        gamma = TranscodingGammaCurve::Get(options.workingGamma, options.defaultGamma);

    // [CLi] TIFF is specified to use associated (= premultiplied) alpha, so that's the preferred mode to use for the image container unless the user overrides
    // (e.g. to handle a non-compliant file).
    bool premul = true;
    if (options.premultiplyOverride)
        premul = options.premultiply;

    // Rather than have libTIFF complain about tags it doesn't understand,
    // we just suppress all the warnings.
    TIFFSetWarningHandler(SuppressTIFFWarnings);
    TIFFSetErrorHandler(SuppressTIFFWarnings);

    // Open and do initial processing
    tif = TIFFClientOpen("Dummy File Name", "r", file,
        Tiff_Read, Tiff_Write, Tiff_Seek, Tiff_Close,
        Tiff_Size, Tiff_Map, Tiff_Unmap);
    if (!tif)
        return (NULL) ;

    // Get basic information about the image
    int ExtraSamples, ExtraSampleInfo;
    TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
    TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
    TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &BitsPerSample);
    TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &RowsPerStrip);
    TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &PhotometricInterpretation);
    TIFFGetField(tif, TIFFTAG_ORIENTATION, &Orientation);
    TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &SamplePerPixel);
    TIFFGetFieldDefaulted(tif, TIFFTAG_EXTRASAMPLES, &ExtraSamples, &ExtraSampleInfo);

    // don't support more than 16 bits per sample
    if (BitsPerSample == 16)
    {
        BytesPerSample = 2 ;
        options.warnings.push_back ("Warning: reading 16 bits/sample TIFF file; components crunched to 8");
    }

    LineSize = TIFFScanlineSize(tif);
    assert (SamplePerPixel == (int) (LineSize / width) / BytesPerSample);
    // SamplePerPixel = (int)(LineSize / width);

#if 0
    // For now we are ignoring the orientation of the image...
    switch (Orientation)
    {
    case ORIENTATION_TOPLEFT:
        break;
    case ORIENTATION_TOPRIGHT:
        break;
    case ORIENTATION_BOTRIGHT:
        break;
    case ORIENTATION_BOTLEFT:
        break;
    case ORIENTATION_LEFTTOP:
        break;
    case ORIENTATION_RIGHTTOP:
        break;
    case ORIENTATION_RIGHTBOT:
        break;
    case ORIENTATION_LEFTBOT:
        break;
    default:
        break;
    }
#endif

    //PhotometricInterpretation = 2 image is RGB
    //PhotometricInterpretation = 3 image have a color palette
    if (PhotometricInterpretation == PHOTOMETRIC_PALETTE && (TIFFIsTiled(tif) == 0))
    {
        uint16 *red, *green, *blue;

        //load the palette
        int cmap_len = (1 << BitsPerSample);

        TIFFGetField(tif, TIFFTAG_COLORMAP, &red, &green, &blue);

        vector<Image::RGBMapEntry> colormap ;
        Image::RGBMapEntry entry;

        // I may be mistaken, but it appears that alpha/opacity information doesn't
        // appear in a Paletted Tiff image.  Well - if it does, it's not as easy to
        // get at as RGB.
        // Read the palette
        // Is the palette 16 or 8 bits ?
        if (checkcmap(cmap_len, red, green, blue) == 16)
        {
            for (int i=0;i<cmap_len;i++)
            {
                entry.red   = IntDecode(gamma, red[i],   65535);
                entry.green = IntDecode(gamma, green[i], 65535);
                entry.blue  = IntDecode(gamma, blue[i],  65535);
                colormap.push_back (entry);
            }
        }
        else
        {
            for (int i=0;i<cmap_len;i++)
            {
                entry.red   = IntDecode(gamma, red[i],   255);
                entry.green = IntDecode(gamma, green[i], 255);
                entry.blue  = IntDecode(gamma, blue[i],  255);
                colormap.push_back (entry);
            }
        }

        Image::ImageDataType imagetype = options.itype;
        if (imagetype == Image::Undefined)
            imagetype = Image::Colour_Map;
        image = Image::Create (width, height, imagetype, colormap) ;
        image->SetPremultiplied(premul); // specify whether the color map data has premultiplied alpha

        boost::scoped_array<unsigned char> buf (new unsigned char [TIFFStripSize(tif)]);

        //read the tiff lines and save them in the image
        //with RGB mode, we have to change the order of the 3 samples RGB <=> BGR
        for (int row=0;row<height;row+=RowsPerStrip)
        {
            nrow = (row + (int)RowsPerStrip > height ? height - row : RowsPerStrip);
            TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, row, 0), buf.get(), nrow * LineSize);
            for (int l = 0, offset = 0; l < nrow ; l++, offset += LineSize)
                for (int x = 0 ; x < width ; x++)
                    image->SetIndexedValue (x, row+l, buf[offset+x]) ;
        }
    }
    else
    {
        // Allocate the row buffers for the image
        boost::scoped_array<uint32> buf (new uint32 [width * height]) ;

        Image::ImageDataType imagetype = options.itype;
        if (imagetype == Image::Undefined)
            imagetype = ( GammaCurve::IsNeutral(gamma) ? Image::RGBA_Int8 : Image::RGBA_Gamma8 );
        image = Image::Create (width, height, imagetype) ;
        image->SetPremultiplied(premul); // set desired storage mode regarding alpha premultiplication
        image->TryDeferDecoding(gamma, 255); // try to have gamma adjustment being deferred until image evaluation.

        TIFFReadRGBAImage(tif, width, height, buf.get(), 0);
        uint32 abgr, *tbuf = buf.get();
        for (int i=height-1;i>=0;i--)
        {
            for (int j=0;j<width;j++)
            {
                abgr = *tbuf++;
                unsigned int b = (unsigned char)TIFFGetB(abgr);
                unsigned int g = (unsigned char)TIFFGetG(abgr);
                unsigned int r = (unsigned char)TIFFGetR(abgr);
                unsigned int a = (unsigned char)TIFFGetA(abgr);
                SetEncodedRGBAValue(image, j, i, gamma, 255, r, g, b, a, premul) ;
            }
        }
    }

    TIFFClose(tif);

    return (image) ;
}
Esempio n. 5
0
// TODO: make sure we don't leak an image object if we throw an exception.
Image *Read (IStream *file, const Image::ReadOptions& options, bool IsPOTFile)
{
	int                             data ;
	int                             width;
	int                             height;
	Image                           *image ;
	unsigned char                   buffer[256];
	vector<Image::RGBAMapEntry>     colormap ;
	int                             alphaIdx = -1; // assume no transparency color

	// GIF files used to have no clearly defined gamma by default, but a W3C recommendation exists for them to use sRGB.
	// Anyway, use whatever the user has chosen as default.
	GammaCurvePtr gamma;
	if (options.gammacorrect)
	{
		if (options.defaultGamma)
			gamma = TranscodingGammaCurve::Get(options.workingGamma, options.defaultGamma);
		else
			gamma = TranscodingGammaCurve::Get(options.workingGamma, SRGBGammaCurve::Get());
	}

	int status = 0;

	/* Get the screen description. */
	if (!file->read (buffer, 13))
		throw POV_EXCEPTION(kFileDataErr, "Cannot read GIF file header");

	/* Use updated GIF specs. */
	if (memcmp ((char *) buffer, "GIF", 3) != 0)
		throw POV_EXCEPTION(kFileDataErr, "File is not in GIF format");

	if (buffer[3] != '8' || (buffer[4] != '7' && buffer[4] != '9') || buffer[5] < 'A' || buffer[5] > 'z')
		throw POV_EXCEPTION(kFileDataErr, "Unsupported GIF version");

	int planes = ((unsigned) buffer [10] & 0x0F) + 1;
	int colourmap_size = (1 << planes);

	/* Color map (better be!) */
	if ((buffer[10] & 0x80) == 0)
		throw POV_EXCEPTION(kFileDataErr, "Error in GIF color map");

	for (int i = 0; i < colourmap_size ; i++)
	{
		Image::RGBAMapEntry entry;
		if (!file->read (buffer, 3))
			throw POV_EXCEPTION(kFileDataErr, "Cannot read GIF colormap");
		entry.red   = IntDecode(gamma, buffer[0], 255);
		entry.green = IntDecode(gamma, buffer[1], 255);
		entry.blue  = IntDecode(gamma, buffer[2], 255);
		entry.alpha = 1.0f;
		colormap.push_back(entry);
	}

	/* Now read one or more GIF objects. */
	bool finished = false;

	while (!finished)
	{
		switch (file->Read_Byte())
		{
			case EOF:
				throw POV_EXCEPTION(kFileDataErr, "Unexpected EOF reading GIF file");
				finished = true;
				break ;

			case ';': /* End of the GIF dataset. */
				finished = true;
				status = 0;
				break;

			case '!': /* GIF Extension Block. */
				/* Read (and check) the ID. */
				if (file->Read_Byte() == 0xF9)
				{
					if ((data = file->Read_Byte()) > 0)
					{
						if (!file->read (buffer, data))
							throw POV_EXCEPTION(kFileDataErr, "Unexpected EOF reading GIF file");
						// check transparency flag, and set transparency color index if appropriate
						if (data >= 3 && buffer[0] & 0x01)
						{
							int alphaIdx = buffer[3];
							if (alphaIdx < colourmap_size)
								colormap[alphaIdx].alpha = 0.0f;
						}
					}
					else
						break;
				}
				while ((data = file->Read_Byte()) > 0)
					if (!file->read (buffer, data))
						throw POV_EXCEPTION(kFileDataErr, "Unexpected EOF reading GIF file");
				break;

			case ',': /* Start of image object. Get description. */
				for (int i = 0; i < 9; i++)
				{
					if ((data = file->Read_Byte()) == EOF)
						throw POV_EXCEPTION(kFileDataErr, "Unexpected EOF reading GIF file");
					buffer[i] = (unsigned char) data;
				}

				/* Check "interlaced" bit. */
				if ((buffer[9] & 0x40) != 0)
					throw POV_EXCEPTION(kFileDataErr, "Interlacing in GIF image unsupported");

				width  = (int) buffer[4] | ((int) buffer[5] << 8);
				height = (int) buffer[6] | ((int) buffer[7] << 8);
				image = Image::Create (width, height, Image::Colour_Map, colormap) ;
				// [CLi] GIF only uses full opacity or full transparency, so premultiplied vs. non-premultiplied alpha is not an issue

				/* Get bytes */
				Decode (file, image);
				finished = true;
				break;

			default:
				status = -1;
				finished = true;
				break;
		}
	}

	if (IsPOTFile == false)
	{
		if (!image)
			throw POV_EXCEPTION(kFileDataErr, "Cannot find GIF image data block");
		return (image);
	}

	// POT files are GIF files where the right half of the image contains
	// a second byte for each pixel on the left, thus allowing 16-bit
	// indexes. In this case the palette data is ignored and we convert
	// the image into a 16-bit grayscale version.
	if ((width & 0x01) != 0)
		throw POV_EXCEPTION(kFileDataErr, "Invalid width for POT file");
	int newWidth = width / 2 ;
	Image *newImage = Image::Create (newWidth, height, Image::Gray_Int16) ;
	for (int y = 0 ; y < height ; y++)
		for (int x = 0 ; x < newWidth ; x++)
			newImage->SetGrayValue (x, y, (unsigned int) image->GetIndexedValue (x, y) << 8 | image->GetIndexedValue (x + newWidth, y)) ;
			// NB: POT files don't use alpha, so premultiplied vs. non-premultiplied is not an issue
			// NB: No gamma adjustment happening here!
	delete image ;
	return (newImage) ;
}