123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include "EditorDefs.h"
- #include "ImageTIF.h"
- /// libTiff
- #include <tiffio.h> // TIFF library
- // Function prototypes
- static tsize_t libtiffDummyReadProc (thandle_t fd, tdata_t buf, tsize_t size);
- static tsize_t libtiffDummyWriteProc (thandle_t fd, tdata_t buf, tsize_t size);
- static toff_t libtiffDummySizeProc(thandle_t fd);
- static toff_t libtiffDummySeekProc (thandle_t fd, toff_t off, int i);
- //static int libtiffDummyCloseProc (thandle_t fd);
- // Structure used to pass state to our in-memory TIFF file callbacks
- struct MemImage
- {
- uint8 *buffer;
- uint32 offset;
- uint32 size;
- };
- /////////////////// Callbacks to libtiff
- static int libtiffDummyMapFileProc(thandle_t, tdata_t*, toff_t*)
- {
- return 0;
- }
- static void libtiffDummyUnmapFileProc(thandle_t, tdata_t, toff_t)
- {
- }
- static toff_t libtiffDummySizeProc(thandle_t fd)
- {
- MemImage *memImage = static_cast<MemImage *>(fd);
- return memImage->size;
- }
- static tsize_t
- libtiffDummyReadProc (thandle_t fd, tdata_t buf, tsize_t size)
- {
- MemImage *memImage = static_cast<MemImage *>(fd);
- tsize_t nBytesLeft = memImage->size - memImage->offset;
- if (size > nBytesLeft)
- {
- size = nBytesLeft;
- }
- memcpy(buf, &memImage->buffer[memImage->offset], size);
- memImage->offset += static_cast<uint32>(size);
- // Return the amount of data read
- return size;
- }
- static tsize_t
- libtiffDummyWriteProc ([[maybe_unused]] thandle_t fd, [[maybe_unused]] tdata_t buf, tsize_t size)
- {
- return (size);
- }
- static toff_t
- libtiffDummySeekProc (thandle_t fd, toff_t off, int i)
- {
- MemImage *memImage = static_cast<MemImage *>(fd);
- switch (i)
- {
- case SEEK_SET:
- memImage->offset = static_cast<uint32>(off);
- break;
- case SEEK_CUR:
- memImage->offset += static_cast<uint32>(off);
- break;
- case SEEK_END:
- memImage->offset = static_cast<uint32>(memImage->size - off);
- break;
- default:
- memImage->offset = static_cast<uint32>(off);
- break;
- }
- // This appears to return the location that it went to
- return memImage->offset;
- }
- static int
- libtiffDummyCloseProc ([[maybe_unused]] thandle_t fd)
- {
- // Return a zero meaning all is well
- return 0;
- }
- bool CImageTIF::Load(const QString& fileName, CImageEx& outImage)
- {
- CCryFile file;
- if (!file.Open(fileName.toUtf8().data(), "rb"))
- {
- CLogFile::FormatLine("File not found %s", fileName.toUtf8().data());
- return false;
- }
- MemImage memImage;
- std::vector<uint8> data;
- memImage.size = static_cast<uint32>(file.GetLength());
- data.resize(memImage.size);
- memImage.buffer = &data[0];
- memImage.offset = 0;
- file.ReadRaw(memImage.buffer, memImage.size);
- // Open the dummy document (which actually only exists in memory)
- TIFF* tif = TIFFClientOpen (fileName.toUtf8().data(), "rm", (thandle_t)&memImage, libtiffDummyReadProc,
- libtiffDummyWriteProc, libtiffDummySeekProc,
- libtiffDummyCloseProc, libtiffDummySizeProc, libtiffDummyMapFileProc, libtiffDummyUnmapFileProc);
- // TIFF* tif = TIFFOpen(fileName,"r");
- bool bRet = false;
- if (tif)
- {
- uint32 dwWidth, dwHeight;
- size_t npixels;
- uint32* raster;
- char* dccfilename = nullptr;
- TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &dwWidth);
- TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &dwHeight);
- TIFFGetField(tif, TIFFTAG_IMAGEDESCRIPTION, &dccfilename);
- npixels = dwWidth * dwHeight;
- raster = (uint32*)_TIFFmalloc((tsize_t)(npixels * sizeof(uint32)));
- if (raster)
- {
- if (TIFFReadRGBAImage(tif, dwWidth, dwHeight, raster, 0))
- {
- if (outImage.Allocate(dwWidth, dwHeight))
- {
- char* dest = (char*)outImage.GetData();
- uint32 dwPitch = dwWidth * 4;
- for (uint32 dwY = 0; dwY < dwHeight; ++dwY)
- {
- char* src2 = (char*)&raster[(dwHeight - 1 - dwY) * dwWidth];
- char* dest2 = &dest[dwPitch * dwY];
- memcpy(dest2, src2, dwWidth * 4);
- }
- if (dccfilename)
- {
- outImage.SetDccFilename(dccfilename);
- }
- bRet = true;
- }
- }
- _TIFFfree(raster);
- }
- TIFFClose(tif);
- }
- if (!bRet)
- {
- outImage.Detach();
- }
- return bRet;
- }
- bool CImageTIF::Load(const QString& fileName, CFloatImage& outImage)
- {
- // Defined in GeoTIFF format - http://web.archive.org/web/20160403164508/http://www.remotesensing.org/geotiff/spec/geotiffhome.html
- // Used to get the X, Y, Z scales from a GeoTIFF file
- static const int GEOTIFF_MODELPIXELSCALE_TAG = 33550;
-
- CCryFile file;
- if (!file.Open(fileName.toUtf8().data(), "rb"))
- {
- CLogFile::FormatLine("File not found %s", fileName.toUtf8().data());
- return false;
- }
- MemImage memImage;
- std::vector<uint8> data;
- memImage.size = static_cast<int>(file.GetLength());
- data.resize(memImage.size);
- memImage.buffer = &data[0];
- memImage.offset = 0;
- file.ReadRaw(memImage.buffer, memImage.size);
- // Open the dummy document (which actually only exists in memory)
- TIFF* tif = TIFFClientOpen(fileName.toUtf8().data(), "rm", (thandle_t)&memImage, libtiffDummyReadProc,
- libtiffDummyWriteProc, libtiffDummySeekProc,
- libtiffDummyCloseProc, libtiffDummySizeProc, libtiffDummyMapFileProc, libtiffDummyUnmapFileProc);
- // TIFF* tif = TIFFOpen(fileName,"r");
- bool bRet = false;
- if (tif)
- {
- uint32 width = 0, height = 0;
- uint16 spp = 0, bpp = 0, format = 0;
- char* dccfilename = nullptr;
- TIFFGetField(tif, TIFFTAG_IMAGEDESCRIPTION, &dccfilename);
- TIFFGetFieldDefaulted(tif, TIFFTAG_IMAGEWIDTH, &width);
- TIFFGetFieldDefaulted(tif, TIFFTAG_IMAGELENGTH, &height);
- TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bpp); // how many bits each color component is. typically 8-bit, but could be 16-bit.
- TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &spp); // how many color components per pixel? 1=greyscale, 3=RGB, 4=RGBA
- TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLEFORMAT, &format); // format of the pixel data - int, uint, float.
- // There are two types of 32-bit floating point TIF semantics. Paint programs tend to use values in the 0.0 - 1.0 range.
- // GeoTIFF files use values where 1.0 = 1 meter by default, but also have an optional ZScale parameter to provide additional
- // scaling control.
- // By default, we'll assume this is a regular TIFF that we want to leave in the 0.0 - 1.0 range.
- float pixelValueScale = 1.0f;
- // Check to see if it's a GeoTIFF, and if so, whether or not it has the ZScale parameter.
- uint32 tagCount = 0;
- double *pixelScales = nullptr;
- if (TIFFGetField(tif, GEOTIFF_MODELPIXELSCALE_TAG, &tagCount, &pixelScales) == 1)
- {
- // if there's an xyz scale, and the Z scale isn't 0, let's use it.
- if ((tagCount == 3) && (pixelScales != nullptr) && (pixelScales[2] != 0.0f))
- {
- pixelValueScale = static_cast<float>(pixelScales[2]);
- }
- }
- uint32 linesize = static_cast<uint32>(TIFFScanlineSize(tif));
- uint8* linebuf = static_cast<uint8*>(_TIFFmalloc(linesize));
- // We assume that a scanline has all of the samples in it. Validate the assumption.
- assert(linesize == (width * (bpp / 8) * spp));
- // Aliases for linebuf to make it easier to pull different types out of the scanline.
- uint16* linebufUint16 = reinterpret_cast<uint16*>(linebuf);
- uint32* linebufUint32 = reinterpret_cast<uint32*>(linebuf);
- float* linebufFloat = reinterpret_cast<float*>(linebuf);
- if (linebuf)
- {
- if (outImage.Allocate(width, height))
- {
- float* dest = outImage.GetData();
- bRet = true;
- float maxPixelValue = 0.0f;
- for (uint32 y = 0; y < height; y++)
- {
- TIFFReadScanline(tif, linebuf, y);
- // For each pixel, we either scale or clamp the values to a 16-bit range. It is asymmetric behaviour, but based
- // on assumptions about the input data:
- // 8-bit values are scaled up because 8-bit textures used as heightmaps are usually scaled-down 16-bit values.
- // 32-bit values may or may not need to scale down, depending on the intended authoring range. Our assumption
- // is that they were most likely authored with the intent of 1:1 value translations.
- for (uint32 x = 0; x < width; x++)
- {
- switch (bpp)
- {
- case 8:
- // Scale 0-255 to 0.0 - 1.0
- dest[(y * width) + x] = static_cast<float>(linebuf[x * spp]) / static_cast<float>(std::numeric_limits<uint8>::max());
- break;
- case 16:
- // Scale 0-65535 to 0.0 - 1.0
- dest[(y * width) + x] = static_cast<float>(linebufUint16[x * spp]) / static_cast<float>(std::numeric_limits<uint16>::max());
- break;
- case 32:
- // 32-bit values could be ints or floats.
- if (format == SAMPLEFORMAT_INT)
- {
- // Scale 0-max int32 to 0.0 - 1.0
- dest[(y * width) + x] = clamp_tpl(static_cast<float>(linebufUint32[x * spp]) / static_cast<float>(std::numeric_limits<int32>::max()), 0.0f, 1.0f);
- }
- else if (format == SAMPLEFORMAT_UINT)
- {
- // Scale 0-max uint32 to 0.0 - 1.0
- dest[(y * width) + x] = clamp_tpl(static_cast<float>(linebufUint32[x * spp]) / static_cast<float>(std::numeric_limits<uint32>::max()), 0.0f, 1.0f);
- }
- else if (format == SAMPLEFORMAT_IEEEFP)
- {
- dest[(y * width) + x] = linebufFloat[x * spp] * pixelValueScale;
- }
- else
- {
- // Unknown / unsupported format.
- bRet = false;
- }
- break;
- default:
- // Unknown / unsupported format.
- bRet = false;
- break;
- }
- maxPixelValue = max(maxPixelValue, dest[(y * width) + x]);
- }
- }
- if (dccfilename)
- {
- outImage.SetDccFilename(dccfilename);
- }
- // If this is a GeoTIFF using 32-bit floats, we will end up outside the 0.0 - 1.0 range. Let's scale it back down to 0.0 - 1.0.
- if (maxPixelValue > 1.0f)
- {
- for (uint32 y = 0; y < height; y++)
- {
- for (uint32 x = 0; x < width; x++)
- {
- dest[(y * width) + x] = dest[(y * width) + x] / maxPixelValue;
- }
- }
- }
- }
- _TIFFfree(linebuf);
- }
- TIFFClose(tif);
- }
- if (!bRet)
- {
- outImage.Detach();
- }
- return bRet;
- }
- //////////////////////////////////////////////////////////////////////////
- bool CImageTIF::SaveRAW(const QString& fileName, const void* pData, int width, int height, int bytesPerChannel, int numChannels, bool bFloat, const char* preset)
- {
- if (bFloat && (bytesPerChannel != 2 && bytesPerChannel != 4))
- {
- bFloat = false;
- }
- bool bRet = false;
- CFileUtil::OverwriteFile(fileName);
- TIFF* tif = TIFFOpen(fileName.toUtf8().data(), "wb");
- if (tif)
- {
- TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width);
- TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height);
- TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, numChannels);
- TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bytesPerChannel * 8);
- TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
- TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, 1);
- TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
- TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, (numChannels == 1) ? PHOTOMETRIC_MINISBLACK : PHOTOMETRIC_RGB);
- TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
- if (bFloat)
- {
- TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP);
- }
- if (preset && preset[0])
- {
- AZStd::string tiffphotoshopdata, valueheader;
- AZStd::string presetkeyvalue = AZStd::string("/preset=") + AZStd::string(preset);
- valueheader.push_back('\x1C');
- valueheader.push_back('\x02');
- valueheader.push_back('\x28');
- valueheader.push_back((presetkeyvalue.size() >> 8) & 0xFF);
- valueheader.push_back((presetkeyvalue.size()) & 0xFF);
- valueheader.append(presetkeyvalue);
- tiffphotoshopdata.push_back('8');
- tiffphotoshopdata.push_back('B');
- tiffphotoshopdata.push_back('I');
- tiffphotoshopdata.push_back('M');
- tiffphotoshopdata.push_back('\x04');
- tiffphotoshopdata.push_back('\x04');
- tiffphotoshopdata.push_back('\x00');
- tiffphotoshopdata.push_back('\x00');
- tiffphotoshopdata.push_back((valueheader.size() >> 24) & 0xFF);
- tiffphotoshopdata.push_back((valueheader.size() >> 16) & 0xFF);
- tiffphotoshopdata.push_back((valueheader.size() >> 8) & 0xFF);
- tiffphotoshopdata.push_back((valueheader.size()) & 0xFF);
- tiffphotoshopdata.append(valueheader);
- TIFFSetField(tif, TIFFTAG_PHOTOSHOP, tiffphotoshopdata.size(), tiffphotoshopdata.c_str());
- }
- size_t pitch = width * bytesPerChannel * numChannels;
- char* raster = (char*) _TIFFmalloc((tsize_t)(pitch * height));
- memcpy(raster, pData, pitch * height);
- bRet = true;
- for (int h = 0; h < height; ++h)
- {
- size_t offset = h * pitch;
- int err = TIFFWriteScanline(tif, raster + offset, h, 0);
- if (err < 0)
- {
- bRet = false;
- break;
- }
- }
- _TIFFfree(raster);
- TIFFClose(tif);
- }
- return bRet;
- }
- const char* CImageTIF::GetPreset(const QString& fileName)
- {
- std::vector<uint8> data;
- CCryFile file;
- if (!file.Open(fileName.toUtf8().data(), "rb"))
- {
- CLogFile::FormatLine("File not found %s", fileName.toUtf8().data());
- return nullptr;
- }
- MemImage memImage;
- memImage.size = static_cast<uint32>(file.GetLength());
- data.resize(memImage.size);
- memImage.buffer = &data[0];
- memImage.offset = 0;
- file.ReadRaw(memImage.buffer, memImage.size);
- TIFF* tif = TIFFClientOpen (fileName.toUtf8().data(), "rm", (thandle_t)&memImage, libtiffDummyReadProc,
- libtiffDummyWriteProc, libtiffDummySeekProc,
- libtiffDummyCloseProc, libtiffDummySizeProc, libtiffDummyMapFileProc, libtiffDummyUnmapFileProc);
- AZStd::string strReturn;
- char* preset = nullptr;
- int size;
- if (tif)
- {
- TIFFGetField(tif, TIFFTAG_PHOTOSHOP, &size, &preset);
- for (int i = 0; i < size; ++i)
- {
- if (!strncmp((preset + i), "preset", 6))
- {
- char* presetoffset = preset + i;
- strReturn = presetoffset;
- if (strReturn.find('/') != -1)
- {
- strReturn = strReturn.substr(0, strReturn.find('/'));
- }
- break;
- }
- }
- TIFFClose(tif);
- }
- return strReturn.c_str();
- }
|