0031687: Draw Harness, ViewerTest - extend command vrenderparams with option updating...
[occt.git] / src / Image / Image_Texture.cxx
CommitLineData
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
24IMPLEMENT_STANDARD_RTTIEXT(Image_Texture, Standard_Transient)
25
26// ================================================================
27// Function : Image_Texture
28// Purpose :
29// ================================================================
30Image_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// ================================================================
46Image_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// ================================================================
66Image_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// ================================================================
82Handle(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 112Handle(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// ================================================================
139Handle(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// ================================================================
153Handle(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// ================================================================
178Handle(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// ================================================================
214TCollection_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// ================================================================
286Standard_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//=======================================================================
346void 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}