Loading files
f2929057eThis file is pinned to the selected release. Download the original to open it in your design tool.
// Copyright (c) 2020 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#include <Image_DDSParser.hxx>
#include <Image_PixMap.hxx>
#include <Image_SupportedFormats.hxx>
#include <Message.hxx>
#include <OSD_FileSystem.hxx>
IMPLEMENT_STANDARD_RTTIEXT(Image_CompressedPixMap, Standard_Transient)
//! DDS Pixel Format structure.
struct Image_DDSParser::DDSPixelFormat
{
uint32_t Size;
uint32_t Flags;
uint32_t FourCC;
uint32_t RGBBitCount;
uint32_t RBitMask;
uint32_t GBitMask;
uint32_t BBitMask;
uint32_t ABitMask;
};
//! DDS File header structure.
struct Image_DDSParser::DDSFileHeader
{
//! Caps2 flag indicating complete (6 faces) cubemap.
enum
{
DDSCompleteCubemap = 0xFE00
};
//! Return TRUE if cubmap flag is set.
bool IscompleteCubemap() const
{
return (Caps2 & DDSFileHeader::DDSCompleteCubemap) == DDSFileHeader::DDSCompleteCubemap;
}
uint32_t Size;
uint32_t Flags;
uint32_t Height;
uint32_t Width;
uint32_t PitchOrLinearSize;
uint32_t Depth;
uint32_t MipMapCount;
uint32_t Reserved1[11];
DDSPixelFormat PixelFormatDef;
uint32_t Caps;
uint32_t Caps2;
uint32_t Caps3;
uint32_t Caps4;
uint32_t Reserved2;
};
//=================================================================================================
occ::handle<Image_CompressedPixMap> Image_DDSParser::Load(
const occ::handle<Image_SupportedFormats>& theSupported,
const TCollection_AsciiString& theFile,
const int theFaceIndex,
const int64_t theFileOffset)
{
const occ::handle<OSD_FileSystem>& aFileSystem = OSD_FileSystem::DefaultFileSystem();
std::shared_ptr<std::istream> aFile =
aFileSystem->OpenIStream(theFile, std::ios::in | std::ios::binary);
char aHeader[128] = {};
if (aFile.get() == nullptr || !aFile->good())
{
return occ::handle<Image_CompressedPixMap>();
}
if (theFileOffset != 0)
{
aFile->seekg((std::streamoff)theFileOffset, std::ios::beg);
}
aFile->read(aHeader, 128);
size_t aNbReadBytes = (size_t)aFile->gcount();
if (aNbReadBytes < 128 || ::memcmp(aHeader, "DDS ", 4) != 0)
{
return occ::handle<Image_CompressedPixMap>();
}
occ::handle<Image_CompressedPixMap> aDef = parseHeader(*(const DDSFileHeader*)(aHeader + 4));
if (aDef.IsNull())
{
return occ::handle<Image_CompressedPixMap>();
}
if (!theSupported.IsNull() && !theSupported->IsSupported(aDef->CompressedFormat()))
{
return occ::handle<Image_CompressedPixMap>();
}
if (theFaceIndex < 0)
{
return aDef;
}
if (theFaceIndex >= aDef->NbFaces() || aDef->FaceBytes() == 0)
{
Message::SendFail(TCollection_AsciiString("DDS Reader error - invalid face index #")
+ theFaceIndex + " within file\n" + theFile);
return occ::handle<Image_CompressedPixMap>();
}
const size_t anOffset = aDef->FaceBytes() * theFaceIndex;
if (anOffset != 0)
{
aFile->seekg((std::streamoff)anOffset, std::ios::cur);
}
occ::handle<NCollection_Buffer> aBuffer =
new NCollection_Buffer(Image_PixMap::DefaultAllocator(), aDef->FaceBytes());
aFile->read((char*)aBuffer->ChangeData(), aDef->FaceBytes());
aNbReadBytes = (size_t)aFile->gcount();
if (aNbReadBytes < aDef->FaceBytes())
{
Message::SendFail(TCollection_AsciiString("DDS Reader error - unable to read face #")
+ theFaceIndex + " data from file\n" + theFile);
return occ::handle<Image_CompressedPixMap>();
}
aDef->SetFaceData(aBuffer);
return aDef;
}
//=================================================================================================
occ::handle<Image_CompressedPixMap> Image_DDSParser::Load(
const occ::handle<Image_SupportedFormats>& theSupported,
const occ::handle<NCollection_Buffer>& theBuffer,
const int theFaceIndex)
{
if (theBuffer.IsNull() || theBuffer->Size() < 128 || ::memcmp(theBuffer->Data(), "DDS ", 4) != 0)
{
return occ::handle<Image_CompressedPixMap>();
}
occ::handle<Image_CompressedPixMap> aDef =
parseHeader(*(const DDSFileHeader*)(theBuffer->Data() + 4));
if (aDef.IsNull())
{
return occ::handle<Image_CompressedPixMap>();
}
if (!theSupported.IsNull() && !theSupported->IsSupported(aDef->CompressedFormat()))
{
return occ::handle<Image_CompressedPixMap>();
}
if (theFaceIndex < 0)
{
return aDef;
}
if (theFaceIndex >= aDef->NbFaces() || aDef->FaceBytes() == 0)
{
Message::SendFail(TCollection_AsciiString("DDS Reader error - invalid face index #")
+ theFaceIndex + " within buffer");
return occ::handle<Image_CompressedPixMap>();
}
const size_t anOffset = aDef->FaceBytes() * theFaceIndex + 128;
if (theBuffer->Size() < anOffset + aDef->FaceBytes())
{
Message::SendFail(TCollection_AsciiString("DDS Reader error - unable to read face #")
+ theFaceIndex + " data from buffer");
return occ::handle<Image_CompressedPixMap>();
}
occ::handle<NCollection_Buffer> aBuffer =
new NCollection_Buffer(Image_PixMap::DefaultAllocator(), aDef->FaceBytes());
memcpy(aBuffer->ChangeData(), theBuffer->Data() + anOffset, aDef->FaceBytes());
aDef->SetFaceData(aBuffer);
return aDef;
}
//=================================================================================================
occ::handle<Image_CompressedPixMap> Image_DDSParser::parseHeader(const DDSFileHeader& theHeader)
{
if (theHeader.Size != 124 || theHeader.Width == 0 || theHeader.Height == 0
|| theHeader.PixelFormatDef.Size != 32)
{
return occ::handle<Image_CompressedPixMap>();
}
Image_Format aBaseFormat = Image_Format_UNKNOWN;
Image_CompressedFormat aFormat = Image_CompressedFormat_UNKNOWN;
int aBlockSize = 8;
const bool hasAlpha = (theHeader.PixelFormatDef.Flags & 0x1) != 0;
if (::memcmp(&theHeader.PixelFormatDef.FourCC, "DXT5", 4) == 0)
{
aBaseFormat = Image_Format_RGBA;
aFormat = Image_CompressedFormat_RGBA_S3TC_DXT5;
aBlockSize = 16;
}
else if (::memcmp(&theHeader.PixelFormatDef.FourCC, "DXT3", 4) == 0)
{
aBaseFormat = Image_Format_RGBA;
aFormat = Image_CompressedFormat_RGBA_S3TC_DXT3;
aBlockSize = 16;
}
else if (::memcmp(&theHeader.PixelFormatDef.FourCC, "DXT1", 4) == 0)
{
aBaseFormat = hasAlpha ? Image_Format_RGBA : Image_Format_RGB;
aFormat =
hasAlpha ? Image_CompressedFormat_RGBA_S3TC_DXT1 : Image_CompressedFormat_RGB_S3TC_DXT1;
aBlockSize = 8;
}
if (aFormat == Image_CompressedFormat_UNKNOWN)
{
return occ::handle<Image_CompressedPixMap>();
}
occ::handle<Image_CompressedPixMap> aDef = new Image_CompressedPixMap();
aDef->SetSize((int)theHeader.Width, (int)theHeader.Height);
aDef->SetNbFaces(theHeader.IscompleteCubemap() != 0 ? 6 : 1);
aDef->SetBaseFormat(aBaseFormat);
aDef->SetCompressedFormat(aFormat);
const int aNbMipMaps = std::max((int)theHeader.MipMapCount, 1);
aDef->ChangeMipMaps().Resize(0, aNbMipMaps - 1, false);
{
size_t aFaceSize = 0;
NCollection_Vec2<int> aMipSizeXY(aDef->SizeX(), aDef->SizeY());
for (int aMipIter = 0;; ++aMipIter)
{
const int aMipLength = ((aMipSizeXY.x() + 3) / 4) * ((aMipSizeXY.y() + 3) / 4) * aBlockSize;
aFaceSize += aMipLength;
aDef->ChangeMipMaps().SetValue(aMipIter, aMipLength);
if (aMipIter + 1 >= aNbMipMaps)
{
break;
}
aMipSizeXY /= 2;
if (aMipSizeXY.x() == 0)
{
aMipSizeXY.x() = 1;
}
if (aMipSizeXY.y() == 0)
{
aMipSizeXY.y() = 1;
}
}
aDef->SetCompleteMipMapSet(aMipSizeXY.x() == 1 && aMipSizeXY.y() == 1);
aDef->SetFaceBytes(aFaceSize);
}
return aDef;
}