// 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 #include #include #include #include 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; }; // ======================================================================= // function : Load // purpose : // ======================================================================= Handle(Image_CompressedPixMap) Image_DDSParser::Load (const Handle(Image_SupportedFormats)& theSupported, const TCollection_AsciiString& theFile, const Standard_Integer theFaceIndex, const int64_t theFileOffset) { std::ifstream aFile; OSD_OpenStream (aFile, theFile.ToCString(), std::ios::in | std::ios::binary); char aHeader[128] = {}; if (!aFile.is_open() || !aFile.good()) { return Handle(Image_CompressedPixMap)(); } if (theFileOffset != 0) { aFile.seekg ((std::streamoff )theFileOffset, std::ios::beg); } aFile.read (aHeader, 128); Standard_Size aNbReadBytes = (Standard_Size )aFile.gcount(); if (aNbReadBytes < 128 || ::memcmp (aHeader, "DDS ", 4) != 0) { return Handle(Image_CompressedPixMap)(); } Handle(Image_CompressedPixMap) aDef = parseHeader (*(const DDSFileHeader* )(aHeader + 4)); if (aDef.IsNull()) { return Handle(Image_CompressedPixMap)(); } if (!theSupported.IsNull() && !theSupported->IsSupported (aDef->CompressedFormat())) { return 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 Handle(Image_CompressedPixMap)(); } const Standard_Size anOffset = aDef->FaceBytes() * theFaceIndex; if (anOffset != 0) { aFile.seekg ((std::streamoff )anOffset, std::ios::cur); } Handle(NCollection_Buffer) aBuffer = new NCollection_Buffer (Image_PixMap::DefaultAllocator(), aDef->FaceBytes()); aFile.read ((char* )aBuffer->ChangeData(), aDef->FaceBytes()); aNbReadBytes = (Standard_Size )aFile.gcount(); if (aNbReadBytes < aDef->FaceBytes()) { Message::SendFail (TCollection_AsciiString ("DDS Reader error - unable to read face #") + theFaceIndex + " data from file\n" + theFile); return Handle(Image_CompressedPixMap)(); } aDef->SetFaceData (aBuffer); return aDef; } // ======================================================================= // function : Load // purpose : // ======================================================================= Handle(Image_CompressedPixMap) Image_DDSParser::Load (const Handle(Image_SupportedFormats)& theSupported, const Handle(NCollection_Buffer)& theBuffer, const Standard_Integer theFaceIndex) { if (theBuffer.IsNull() || theBuffer->Size() < 128 || ::memcmp (theBuffer->Data(), "DDS ", 4) != 0) { return Handle(Image_CompressedPixMap)(); } Handle(Image_CompressedPixMap) aDef = parseHeader (*(const DDSFileHeader* )(theBuffer->Data() + 4)); if (aDef.IsNull()) { return Handle(Image_CompressedPixMap)(); } if (!theSupported.IsNull() && !theSupported->IsSupported (aDef->CompressedFormat())) { return 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 Handle(Image_CompressedPixMap)(); } const Standard_Size 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 Handle(Image_CompressedPixMap)(); } 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; } // ======================================================================= // function : parseHeader // purpose : // ======================================================================= Handle(Image_CompressedPixMap) Image_DDSParser::parseHeader (const DDSFileHeader& theHeader) { if (theHeader.Size != 124 || theHeader.Width == 0 || theHeader.Height == 0 || theHeader.PixelFormatDef.Size != 32) { return Handle(Image_CompressedPixMap)(); } Image_Format aBaseFormat = Image_Format_UNKNOWN; Image_CompressedFormat aFormat = Image_CompressedFormat_UNKNOWN; Standard_Integer 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 Handle(Image_CompressedPixMap)(); } Handle(Image_CompressedPixMap) aDef = new Image_CompressedPixMap(); aDef->SetSize ((Standard_Integer )theHeader.Width, (Standard_Integer )theHeader.Height); aDef->SetNbFaces (theHeader.IscompleteCubemap() != 0 ? 6 : 1); aDef->SetBaseFormat (aBaseFormat); aDef->SetCompressedFormat (aFormat); const Standard_Integer aNbMipMaps = Max ((Standard_Integer )theHeader.MipMapCount, 1); aDef->ChangeMipMaps().Resize (0, aNbMipMaps - 1, false); { Standard_Size aFaceSize = 0; NCollection_Vec2 aMipSizeXY (aDef->SizeX(), aDef->SizeY()); for (Standard_Integer aMipIter = 0;; ++aMipIter) { const Standard_Integer 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; }