0032455: Data Exchange - replace OSD_OpenStream() usage with OSD_FileSystem::DefaultF...
[occt.git] / src / Image / Image_DDSParser.cxx
1 // Copyright (c) 2020 OPEN CASCADE SAS
2 //
3 // This file is part of Open CASCADE Technology software library.
4 //
5 // This library is free software; you can redistribute it and/or modify it under
6 // the terms of the GNU Lesser General Public License version 2.1 as published
7 // by the Free Software Foundation, with special exception defined in the file
8 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
9 // distribution for complete text of the license and disclaimer of any warranty.
10 //
11 // Alternatively, this file may be used under the terms of Open CASCADE
12 // commercial license or contractual agreement.
13
14 #include <Image_DDSParser.hxx>
15
16 #include <Image_PixMap.hxx>
17 #include <Image_SupportedFormats.hxx>
18 #include <Message.hxx>
19 #include <OSD_FileSystem.hxx>
20
21 IMPLEMENT_STANDARD_RTTIEXT(Image_CompressedPixMap, Standard_Transient)
22
23 //! DDS Pixel Format structure.
24 struct Image_DDSParser::DDSPixelFormat
25 {
26   uint32_t Size;
27   uint32_t Flags;
28   uint32_t FourCC;
29   uint32_t RGBBitCount;
30   uint32_t RBitMask;
31   uint32_t GBitMask;
32   uint32_t BBitMask;
33   uint32_t ABitMask;
34 };
35
36 //! DDS File header structure.
37 struct Image_DDSParser::DDSFileHeader
38 {
39   //! Caps2 flag indicating complete (6 faces) cubemap.
40   enum { DDSCompleteCubemap = 0xFE00 };
41
42   //! Return TRUE if cubmap flag is set.
43   bool IscompleteCubemap() const { return (Caps2 & DDSFileHeader::DDSCompleteCubemap) == DDSFileHeader::DDSCompleteCubemap; }
44
45   uint32_t Size;
46   uint32_t Flags;
47   uint32_t Height;
48   uint32_t Width;
49   uint32_t PitchOrLinearSize;
50   uint32_t Depth;
51   uint32_t MipMapCount;
52   uint32_t Reserved1[11];
53   DDSPixelFormat PixelFormatDef;
54   uint32_t Caps;
55   uint32_t Caps2;
56   uint32_t Caps3;
57   uint32_t Caps4;
58   uint32_t Reserved2;
59 };
60
61 // =======================================================================
62 // function : Load
63 // purpose  :
64 // =======================================================================
65 Handle(Image_CompressedPixMap) Image_DDSParser::Load (const Handle(Image_SupportedFormats)& theSupported,
66                                                       const TCollection_AsciiString& theFile,
67                                                       const Standard_Integer theFaceIndex,
68                                                       const int64_t theFileOffset)
69 {
70   const Handle(OSD_FileSystem)& aFileSystem = OSD_FileSystem::DefaultFileSystem();
71   opencascade::std::shared_ptr<std::istream> aFile = aFileSystem->OpenIStream (theFile, std::ios::in | std::ios::binary);
72   char aHeader[128] = {};
73   if (aFile.get() == NULL || !aFile->good())
74   {
75     return Handle(Image_CompressedPixMap)();
76   }
77   if (theFileOffset != 0)
78   {
79     aFile->seekg ((std::streamoff )theFileOffset, std::ios::beg);
80   }
81   aFile->read (aHeader, 128);
82   Standard_Size aNbReadBytes = (Standard_Size )aFile->gcount();
83   if (aNbReadBytes < 128
84    || ::memcmp (aHeader, "DDS ", 4) != 0)
85   {
86     return Handle(Image_CompressedPixMap)();
87   }
88
89   Handle(Image_CompressedPixMap) aDef = parseHeader (*(const DDSFileHeader* )(aHeader + 4));
90   if (aDef.IsNull())
91   {
92     return Handle(Image_CompressedPixMap)();
93   }
94
95   if (!theSupported.IsNull()
96    && !theSupported->IsSupported (aDef->CompressedFormat()))
97   {
98     return Handle(Image_CompressedPixMap)();
99   }
100
101   if (theFaceIndex < 0)
102   {
103     return aDef;
104   }
105
106   if (theFaceIndex >= aDef->NbFaces()
107    || aDef->FaceBytes() == 0)
108   {
109     Message::SendFail (TCollection_AsciiString ("DDS Reader error - invalid face index #") + theFaceIndex + " within file\n" + theFile);
110     return Handle(Image_CompressedPixMap)();
111   }
112
113   const Standard_Size anOffset = aDef->FaceBytes() * theFaceIndex;
114   if (anOffset != 0)
115   {
116     aFile->seekg ((std::streamoff )anOffset, std::ios::cur);
117   }
118   Handle(NCollection_Buffer) aBuffer = new NCollection_Buffer (Image_PixMap::DefaultAllocator(), aDef->FaceBytes());
119   aFile->read ((char* )aBuffer->ChangeData(), aDef->FaceBytes());
120   aNbReadBytes = (Standard_Size )aFile->gcount();
121   if (aNbReadBytes < aDef->FaceBytes())
122   {
123     Message::SendFail (TCollection_AsciiString ("DDS Reader error - unable to read face #") + theFaceIndex + " data from file\n" + theFile);
124     return Handle(Image_CompressedPixMap)();
125   }
126   aDef->SetFaceData (aBuffer);
127   return aDef;
128 }
129
130 // =======================================================================
131 // function : Load
132 // purpose  :
133 // =======================================================================
134 Handle(Image_CompressedPixMap) Image_DDSParser::Load (const Handle(Image_SupportedFormats)& theSupported,
135                                                       const Handle(NCollection_Buffer)& theBuffer,
136                                                       const Standard_Integer theFaceIndex)
137 {
138   if (theBuffer.IsNull()
139    || theBuffer->Size() < 128
140    || ::memcmp (theBuffer->Data(), "DDS ", 4) != 0)
141   {
142     return Handle(Image_CompressedPixMap)();
143   }
144
145   Handle(Image_CompressedPixMap) aDef = parseHeader (*(const DDSFileHeader* )(theBuffer->Data() + 4));
146   if (aDef.IsNull())
147   {
148     return Handle(Image_CompressedPixMap)();
149   }
150
151   if (!theSupported.IsNull()
152    && !theSupported->IsSupported (aDef->CompressedFormat()))
153   {
154     return Handle(Image_CompressedPixMap)();
155   }
156
157   if (theFaceIndex < 0)
158   {
159     return aDef;
160   }
161
162   if (theFaceIndex >= aDef->NbFaces()
163    || aDef->FaceBytes() == 0)
164   {
165     Message::SendFail (TCollection_AsciiString ("DDS Reader error - invalid face index #") + theFaceIndex + " within buffer");
166     return Handle(Image_CompressedPixMap)();
167   }
168
169   const Standard_Size anOffset = aDef->FaceBytes() * theFaceIndex + 128;
170   if (theBuffer->Size() < anOffset + aDef->FaceBytes())
171   {
172     Message::SendFail (TCollection_AsciiString ("DDS Reader error - unable to read face #") + theFaceIndex + " data from buffer");
173     return Handle(Image_CompressedPixMap)();
174   }
175
176   Handle(NCollection_Buffer) aBuffer = new NCollection_Buffer (Image_PixMap::DefaultAllocator(), aDef->FaceBytes());
177   memcpy (aBuffer->ChangeData(), theBuffer->Data() + anOffset, aDef->FaceBytes());
178   aDef->SetFaceData (aBuffer);
179   return aDef;
180 }
181
182 // =======================================================================
183 // function : parseHeader
184 // purpose  :
185 // =======================================================================
186 Handle(Image_CompressedPixMap) Image_DDSParser::parseHeader (const DDSFileHeader& theHeader)
187 {
188   if (theHeader.Size != 124
189    || theHeader.Width  == 0
190    || theHeader.Height == 0
191    || theHeader.PixelFormatDef.Size != 32)
192   {
193     return Handle(Image_CompressedPixMap)();
194   }
195
196   Image_Format aBaseFormat = Image_Format_UNKNOWN;
197   Image_CompressedFormat aFormat = Image_CompressedFormat_UNKNOWN;
198   Standard_Integer aBlockSize = 8;
199   const bool hasAlpha = (theHeader.PixelFormatDef.Flags & 0x1) != 0;
200   if (::memcmp (&theHeader.PixelFormatDef.FourCC, "DXT5", 4) == 0)
201   {
202     aBaseFormat = Image_Format_RGBA;
203     aFormat = Image_CompressedFormat_RGBA_S3TC_DXT5;
204     aBlockSize = 16;
205   }
206   else if (::memcmp (&theHeader.PixelFormatDef.FourCC, "DXT3", 4) == 0)
207   {
208     aBaseFormat = Image_Format_RGBA;
209     aFormat = Image_CompressedFormat_RGBA_S3TC_DXT3;
210     aBlockSize = 16;
211   }
212   else if (::memcmp (&theHeader.PixelFormatDef.FourCC, "DXT1", 4) == 0)
213   {
214     aBaseFormat = hasAlpha ? Image_Format_RGBA : Image_Format_RGB;
215     aFormat = hasAlpha ? Image_CompressedFormat_RGBA_S3TC_DXT1 : Image_CompressedFormat_RGB_S3TC_DXT1;
216     aBlockSize = 8;
217   }
218   if (aFormat == Image_CompressedFormat_UNKNOWN)
219   {
220     return Handle(Image_CompressedPixMap)();
221   }
222
223   Handle(Image_CompressedPixMap) aDef = new Image_CompressedPixMap();
224   aDef->SetSize ((Standard_Integer )theHeader.Width, (Standard_Integer )theHeader.Height);
225   aDef->SetNbFaces (theHeader.IscompleteCubemap() != 0 ? 6 : 1);
226   aDef->SetBaseFormat (aBaseFormat);
227   aDef->SetCompressedFormat (aFormat);
228
229   const Standard_Integer aNbMipMaps = Max ((Standard_Integer )theHeader.MipMapCount, 1);
230   aDef->ChangeMipMaps().Resize (0, aNbMipMaps - 1, false);
231   {
232     Standard_Size aFaceSize = 0;
233     NCollection_Vec2<Standard_Integer> aMipSizeXY (aDef->SizeX(), aDef->SizeY());
234     for (Standard_Integer aMipIter = 0;; ++aMipIter)
235     {
236       const Standard_Integer aMipLength = ((aMipSizeXY.x() + 3) / 4) * ((aMipSizeXY.y() + 3) / 4) * aBlockSize;
237       aFaceSize += aMipLength;
238       aDef->ChangeMipMaps().SetValue (aMipIter, aMipLength);
239       if (aMipIter + 1 >= aNbMipMaps)
240       {
241         break;
242       }
243
244       aMipSizeXY /= 2;
245       if (aMipSizeXY.x() == 0) { aMipSizeXY.x() = 1; }
246       if (aMipSizeXY.y() == 0) { aMipSizeXY.y() = 1; }
247     }
248     aDef->SetCompleteMipMapSet (aMipSizeXY.x() == 1 && aMipSizeXY.y() == 1);
249     aDef->SetFaceBytes (aFaceSize);
250   }
251
252   return aDef;
253 }