fc552d84 |
1 | // Author: Kirill Gavrilov |
2 | // Copyright (c) 2015-2019 OPEN CASCADE SAS |
3 | // |
4 | // This file is part of Open CASCADE Technology software library. |
5 | // |
6 | // This library is free software; you can redistribute it and/or modify it under |
7 | // the terms of the GNU Lesser General Public License version 2.1 as published |
8 | // by the Free Software Foundation, with special exception defined in the file |
9 | // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT |
10 | // distribution for complete text of the license and disclaimer of any warranty. |
11 | // |
12 | // Alternatively, this file may be used under the terms of Open CASCADE |
13 | // commercial license or contractual agreement. |
14 | |
15 | #include <Image_Texture.hxx> |
16 | |
17 | #include <Image_AlienPixMap.hxx> |
faff3767 |
18 | #include <Image_DDSParser.hxx> |
19 | #include <Image_SupportedFormats.hxx> |
fc552d84 |
20 | #include <Message.hxx> |
21 | #include <Message_Messenger.hxx> |
22 | #include <OSD_OpenFile.hxx> |
23 | |
24 | IMPLEMENT_STANDARD_RTTIEXT(Image_Texture, Standard_Transient) |
25 | |
26 | // ================================================================ |
27 | // Function : Image_Texture |
28 | // Purpose : |
29 | // ================================================================ |
30 | Image_Texture::Image_Texture (const TCollection_AsciiString& theFileName) |
31 | : myImagePath (theFileName), |
32 | myOffset (-1), |
33 | myLength (-1) |
34 | { |
35 | // share textures with unique file paths |
36 | if (!theFileName.IsEmpty()) |
37 | { |
38 | myTextureId = TCollection_AsciiString ("texture://") + theFileName; |
39 | } |
40 | } |
41 | |
42 | // ================================================================ |
43 | // Function : Image_Texture |
44 | // Purpose : |
45 | // ================================================================ |
46 | Image_Texture::Image_Texture (const TCollection_AsciiString& theFileName, |
47 | int64_t theOffset, |
48 | int64_t theLength) |
49 | : myImagePath (theFileName), |
50 | myOffset (theOffset), |
51 | myLength (theLength) |
52 | { |
53 | // share textures with unique file paths |
54 | if (!theFileName.IsEmpty()) |
55 | { |
56 | char aBuff[60]; |
57 | Sprintf (aBuff, ";%" PRId64 ",%" PRId64, theOffset, theLength); |
58 | myTextureId = TCollection_AsciiString ("texture://") + theFileName + aBuff; |
59 | } |
60 | } |
61 | |
62 | // ================================================================ |
63 | // Function : Image_Texture |
64 | // Purpose : |
65 | // ================================================================ |
66 | Image_Texture::Image_Texture (const Handle(NCollection_Buffer)& theBuffer, |
67 | const TCollection_AsciiString& theId) |
68 | : myBuffer (theBuffer), |
69 | myOffset (-1), |
70 | myLength (-1) |
71 | { |
72 | if (!theId.IsEmpty()) |
73 | { |
74 | myTextureId = TCollection_AsciiString ("texturebuf://") + theId; |
75 | } |
76 | } |
77 | |
faff3767 |
78 | // ================================================================ |
79 | // Function : ReadCompressedImage |
80 | // Purpose : |
81 | // ================================================================ |
82 | Handle(Image_CompressedPixMap) Image_Texture::ReadCompressedImage (const Handle(Image_SupportedFormats)& theSupported) const |
83 | { |
84 | if (!theSupported->HasCompressed()) |
85 | { |
86 | return Handle(Image_CompressedPixMap)(); |
87 | } |
88 | |
89 | if (!myBuffer.IsNull()) |
90 | { |
91 | return Image_DDSParser::Load (theSupported, myBuffer, 0); |
92 | } |
93 | else if (myOffset >= 0) |
94 | { |
95 | return Image_DDSParser::Load (theSupported, myImagePath, 0, myOffset); |
96 | } |
97 | |
98 | TCollection_AsciiString aFilePathLower = myImagePath; |
99 | aFilePathLower.LowerCase(); |
100 | if (!aFilePathLower.EndsWith (".dds")) |
101 | { |
102 | // do not waste time on file system access in case of wrong file extension |
103 | return Handle(Image_CompressedPixMap)(); |
104 | } |
105 | return Image_DDSParser::Load (theSupported, myImagePath, 0); |
106 | } |
107 | |
fc552d84 |
108 | // ================================================================ |
109 | // Function : ReadImage |
110 | // Purpose : |
111 | // ================================================================ |
faff3767 |
112 | Handle(Image_PixMap) Image_Texture::ReadImage (const Handle(Image_SupportedFormats)& ) const |
fc552d84 |
113 | { |
114 | Handle(Image_PixMap) anImage; |
115 | if (!myBuffer.IsNull()) |
116 | { |
117 | anImage = loadImageBuffer (myBuffer, myTextureId); |
118 | } |
119 | else if (myOffset >= 0) |
120 | { |
121 | anImage = loadImageOffset (myImagePath, myOffset, myLength); |
122 | } |
123 | else |
124 | { |
125 | anImage = loadImageFile (myImagePath); |
126 | } |
127 | |
128 | if (anImage.IsNull()) |
129 | { |
130 | return Handle(Image_PixMap)(); |
131 | } |
132 | return anImage; |
133 | } |
134 | |
135 | // ================================================================ |
136 | // Function : loadImageFile |
137 | // Purpose : |
138 | // ================================================================ |
139 | Handle(Image_PixMap) Image_Texture::loadImageFile (const TCollection_AsciiString& thePath) const |
140 | { |
141 | Handle(Image_AlienPixMap) anImage = new Image_AlienPixMap(); |
142 | if (!anImage->Load (thePath)) |
143 | { |
144 | return Handle(Image_PixMap)(); |
145 | } |
146 | return anImage; |
147 | } |
148 | |
149 | // ================================================================ |
150 | // Function : loadImageBuffer |
151 | // Purpose : |
152 | // ================================================================ |
153 | Handle(Image_PixMap) Image_Texture::loadImageBuffer (const Handle(NCollection_Buffer)& theBuffer, |
154 | const TCollection_AsciiString& theId) const |
155 | { |
156 | if (theBuffer.IsNull()) |
157 | { |
158 | return Handle(Image_PixMap)(); |
159 | } |
160 | else if (theBuffer->Size() > (Standard_Size )IntegerLast()) |
161 | { |
a87b1b37 |
162 | Message::SendFail (TCollection_AsciiString ("Error: Image file size is too big '") + theId + "'"); |
fc552d84 |
163 | return Handle(Image_PixMap)(); |
164 | } |
165 | |
166 | Handle(Image_AlienPixMap) anImage = new Image_AlienPixMap(); |
167 | if (!anImage->Load (theBuffer->Data(), (int )theBuffer->Size(), theId)) |
168 | { |
169 | return Handle(Image_PixMap)(); |
170 | } |
171 | return anImage; |
172 | } |
173 | |
174 | // ================================================================ |
175 | // Function : loadImageOffset |
176 | // Purpose : |
177 | // ================================================================ |
178 | Handle(Image_PixMap) Image_Texture::loadImageOffset (const TCollection_AsciiString& thePath, |
179 | int64_t theOffset, |
180 | int64_t theLength) const |
181 | { |
182 | if (theLength > IntegerLast()) |
183 | { |
a87b1b37 |
184 | Message::SendFail (TCollection_AsciiString ("Error: Image file size is too big '") + thePath + "'"); |
fc552d84 |
185 | return Handle(Image_PixMap)(); |
186 | } |
187 | |
188 | std::ifstream aFile; |
189 | OSD_OpenStream (aFile, thePath.ToCString(), std::ios::in | std::ios::binary); |
190 | if (!aFile) |
191 | { |
a87b1b37 |
192 | Message::SendFail (TCollection_AsciiString ("Error: Image file '") + thePath + "' cannot be opened"); |
fc552d84 |
193 | return Handle(Image_PixMap)(); |
194 | } |
195 | aFile.seekg ((std::streamoff )theOffset, std::ios_base::beg); |
196 | if (!aFile.good()) |
197 | { |
a87b1b37 |
198 | Message::SendFail (TCollection_AsciiString ("Error: Image is defined with invalid file offset '") + thePath + "'"); |
fc552d84 |
199 | return Handle(Image_PixMap)(); |
200 | } |
201 | |
202 | Handle(Image_AlienPixMap) anImage = new Image_AlienPixMap(); |
203 | if (!anImage->Load (aFile, thePath)) |
204 | { |
205 | return Handle(Image_PixMap)(); |
206 | } |
207 | return anImage; |
208 | } |
209 | |
210 | // ================================================================ |
211 | // Function : ProbeImageFileFormat |
212 | // Purpose : |
213 | // ================================================================ |
214 | TCollection_AsciiString Image_Texture::ProbeImageFileFormat() const |
215 | { |
1bd04b5a |
216 | static const Standard_Size THE_PROBE_SIZE = 20; |
fc552d84 |
217 | char aBuffer[THE_PROBE_SIZE]; |
218 | if (!myBuffer.IsNull()) |
219 | { |
220 | memcpy (aBuffer, myBuffer->Data(), myBuffer->Size() < THE_PROBE_SIZE ? myBuffer->Size() : THE_PROBE_SIZE); |
221 | } |
222 | else |
223 | { |
224 | std::ifstream aFileIn; |
225 | OSD_OpenStream (aFileIn, myImagePath.ToCString(), std::ios::in | std::ios::binary); |
226 | if (!aFileIn) |
227 | { |
a87b1b37 |
228 | Message::SendFail (TCollection_AsciiString ("Error: Unable to open file '") + myImagePath + "'"); |
fc552d84 |
229 | return false; |
230 | } |
231 | if (myOffset >= 0) |
232 | { |
233 | aFileIn.seekg ((std::streamoff )myOffset, std::ios_base::beg); |
234 | if (!aFileIn.good()) |
235 | { |
a87b1b37 |
236 | Message::SendFail (TCollection_AsciiString ("Error: Image is defined with invalid file offset '") + myImagePath + "'"); |
fc552d84 |
237 | return false; |
238 | } |
239 | } |
240 | |
241 | if (!aFileIn.read (aBuffer, THE_PROBE_SIZE)) |
242 | { |
a87b1b37 |
243 | Message::SendFail (TCollection_AsciiString ("Error: unable to read image file '") + myImagePath + "'"); |
fc552d84 |
244 | return false; |
245 | } |
246 | } |
247 | |
248 | if (memcmp (aBuffer, "\x89" "PNG\r\n" "\x1A" "\n", 8) == 0) |
249 | { |
250 | return "png"; |
251 | } |
252 | else if (memcmp (aBuffer, "\xFF\xD8\xFF", 3) == 0) |
253 | { |
254 | return "jpg"; |
255 | } |
256 | else if (memcmp (aBuffer, "GIF87a", 6) == 0 |
257 | || memcmp (aBuffer, "GIF89a", 6) == 0) |
258 | { |
259 | return "gif"; |
260 | } |
261 | else if (memcmp (aBuffer, "II\x2A\x00", 4) == 0 |
262 | || memcmp (aBuffer, "MM\x00\x2A", 4) == 0) |
263 | { |
264 | return "tiff"; |
265 | } |
266 | else if (memcmp (aBuffer, "BM", 2) == 0) |
267 | { |
268 | return "bmp"; |
269 | } |
270 | else if (memcmp (aBuffer, "RIFF", 4) == 0 |
271 | && memcmp (aBuffer + 8, "WEBP", 4) == 0) |
272 | { |
273 | return "webp"; |
274 | } |
faff3767 |
275 | else if (memcmp (aBuffer, "DDS ", 4) == 0) |
276 | { |
277 | return "dds"; |
278 | } |
fc552d84 |
279 | return ""; |
280 | } |
281 | |
282 | // ================================================================ |
283 | // Function : WriteImage |
284 | // Purpose : |
285 | // ================================================================ |
286 | Standard_Boolean Image_Texture::WriteImage (const TCollection_AsciiString& theFile) |
287 | { |
288 | Handle(NCollection_Buffer) aBuffer = myBuffer; |
289 | if (myBuffer.IsNull()) |
290 | { |
291 | std::ifstream aFileIn; |
292 | OSD_OpenStream (aFileIn, myImagePath.ToCString(), std::ios::in | std::ios::binary); |
293 | if (!aFileIn) |
294 | { |
a87b1b37 |
295 | Message::SendFail (TCollection_AsciiString ("Error: Unable to open file ") + myImagePath + "!"); |
fc552d84 |
296 | return Standard_False; |
297 | } |
298 | |
299 | Standard_Size aLen = (Standard_Size )myLength; |
300 | if (myOffset >= 0) |
301 | { |
302 | aFileIn.seekg ((std::streamoff )myOffset, std::ios_base::beg); |
303 | if (!aFileIn.good()) |
304 | { |
a87b1b37 |
305 | Message::SendFail (TCollection_AsciiString ("Error: Image is defined with invalid file offset '") + myImagePath + "'"); |
fc552d84 |
306 | return Standard_False; |
307 | } |
308 | } |
309 | else |
310 | { |
311 | aFileIn.seekg (0, std::ios_base::end); |
312 | aLen = (Standard_Size )aFileIn.tellg(); |
313 | aFileIn.seekg (0, std::ios_base::beg); |
314 | } |
315 | |
316 | aBuffer = new NCollection_Buffer (NCollection_BaseAllocator::CommonBaseAllocator(), aLen); |
317 | if (!aFileIn.read ((char* )aBuffer->ChangeData(), aBuffer->Size())) |
318 | { |
a87b1b37 |
319 | Message::SendFail (TCollection_AsciiString ("Error: unable to read image file '") + myImagePath + "'"); |
fc552d84 |
320 | return Standard_False; |
321 | } |
322 | } |
323 | |
324 | std::ofstream aFileOut; |
325 | OSD_OpenStream (aFileOut, theFile.ToCString(), std::ios::out | std::ios::binary | std::ios::trunc); |
326 | if (!aFileOut) |
327 | { |
a87b1b37 |
328 | Message::SendFail (TCollection_AsciiString ("Error: Unable to create file '") + theFile + "'"); |
fc552d84 |
329 | return Standard_False; |
330 | } |
331 | |
332 | aFileOut.write ((const char* )aBuffer->Data(), aBuffer->Size()); |
333 | aFileOut.close(); |
334 | if (!aFileOut.good()) |
335 | { |
a87b1b37 |
336 | Message::SendFail (TCollection_AsciiString ("Error: Unable to write file '") + theFile + "'"); |
fc552d84 |
337 | return Standard_False; |
338 | } |
339 | return Standard_True; |
340 | } |
bc73b006 |
341 | |
342 | //======================================================================= |
343 | //function : DumpJson |
344 | //purpose : |
345 | //======================================================================= |
346 | void Image_Texture::DumpJson (Standard_OStream& theOStream, Standard_Integer theDepth) const |
347 | { |
348 | OCCT_DUMP_TRANSIENT_CLASS_BEGIN (theOStream) |
349 | |
350 | OCCT_DUMP_FIELD_VALUE_STRING (theOStream, myTextureId) |
351 | OCCT_DUMP_FIELD_VALUE_STRING (theOStream, myImagePath) |
352 | |
353 | OCCT_DUMP_FIELD_VALUES_DUMPED (theOStream, theDepth, myBuffer.get()) |
354 | |
355 | OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myOffset) |
356 | OCCT_DUMP_FIELD_VALUE_NUMERICAL (theOStream, myLength) |
357 | } |