0030692: Data Exchange - introduce base framework RWMesh for importing mesh data...
[occt.git] / src / Image / Image_Texture.cxx
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>
18 #include <Message.hxx>
19 #include <Message_Messenger.hxx>
20 #include <OSD_OpenFile.hxx>
21
22 IMPLEMENT_STANDARD_RTTIEXT(Image_Texture, Standard_Transient)
23
24 // ================================================================
25 // Function : Image_Texture
26 // Purpose  :
27 // ================================================================
28 Image_Texture::Image_Texture (const TCollection_AsciiString& theFileName)
29 : myImagePath (theFileName),
30   myOffset (-1),
31   myLength (-1)
32 {
33   // share textures with unique file paths
34   if (!theFileName.IsEmpty())
35   {
36     myTextureId = TCollection_AsciiString ("texture://") + theFileName;
37   }
38 }
39
40 // ================================================================
41 // Function : Image_Texture
42 // Purpose  :
43 // ================================================================
44 Image_Texture::Image_Texture (const TCollection_AsciiString& theFileName,
45                               int64_t theOffset,
46                               int64_t theLength)
47 : myImagePath (theFileName),
48   myOffset (theOffset),
49   myLength (theLength)
50 {
51   // share textures with unique file paths
52   if (!theFileName.IsEmpty())
53   {
54     char aBuff[60];
55     Sprintf (aBuff, ";%" PRId64 ",%" PRId64, theOffset, theLength);
56     myTextureId = TCollection_AsciiString ("texture://") + theFileName + aBuff;
57   }
58 }
59
60 // ================================================================
61 // Function : Image_Texture
62 // Purpose  :
63 // ================================================================
64 Image_Texture::Image_Texture (const Handle(NCollection_Buffer)& theBuffer,
65                               const TCollection_AsciiString& theId)
66 : myBuffer (theBuffer),
67   myOffset (-1),
68   myLength (-1)
69 {
70   if (!theId.IsEmpty())
71   {
72     myTextureId = TCollection_AsciiString ("texturebuf://") + theId;
73   }
74 }
75
76 // ================================================================
77 // Function : ReadImage
78 // Purpose  :
79 // ================================================================
80 Handle(Image_PixMap) Image_Texture::ReadImage() const
81 {
82   Handle(Image_PixMap) anImage;
83   if (!myBuffer.IsNull())
84   {
85     anImage = loadImageBuffer (myBuffer, myTextureId);
86   }
87   else if (myOffset >= 0)
88   {
89     anImage = loadImageOffset (myImagePath, myOffset, myLength);
90   }
91   else
92   {
93     anImage = loadImageFile (myImagePath);
94   }
95
96   if (anImage.IsNull())
97   {
98     return Handle(Image_PixMap)();
99   }
100   return anImage;
101 }
102
103 // ================================================================
104 // Function : loadImageFile
105 // Purpose  :
106 // ================================================================
107 Handle(Image_PixMap) Image_Texture::loadImageFile (const TCollection_AsciiString& thePath) const
108 {
109   Handle(Image_AlienPixMap) anImage = new Image_AlienPixMap();
110   if (!anImage->Load (thePath))
111   {
112     return Handle(Image_PixMap)();
113   }
114   return anImage;
115 }
116
117 // ================================================================
118 // Function : loadImageBuffer
119 // Purpose  :
120 // ================================================================
121 Handle(Image_PixMap) Image_Texture::loadImageBuffer (const Handle(NCollection_Buffer)& theBuffer,
122                                                      const TCollection_AsciiString& theId) const
123 {
124   if (theBuffer.IsNull())
125   {
126     return Handle(Image_PixMap)();
127   }
128   else if (theBuffer->Size() > (Standard_Size )IntegerLast())
129   {
130     Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: Image file size is too big '") + theId + "'.", Message_Fail);
131     return Handle(Image_PixMap)();
132   }
133
134   Handle(Image_AlienPixMap) anImage = new Image_AlienPixMap();
135   if (!anImage->Load (theBuffer->Data(), (int )theBuffer->Size(), theId))
136   {
137     return Handle(Image_PixMap)();
138   }
139   return anImage;
140 }
141
142 // ================================================================
143 // Function : loadImageOffset
144 // Purpose  :
145 // ================================================================
146 Handle(Image_PixMap) Image_Texture::loadImageOffset (const TCollection_AsciiString& thePath,
147                                                      int64_t theOffset,
148                                                      int64_t theLength) const
149 {
150   if (theLength > IntegerLast())
151   {
152     Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: Image file size is too big '") + thePath + "'.", Message_Fail);
153     return Handle(Image_PixMap)();
154   }
155
156   std::ifstream aFile;
157   OSD_OpenStream (aFile, thePath.ToCString(), std::ios::in | std::ios::binary);
158   if (!aFile)
159   {
160     Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: Image file '") + thePath + "' cannot be opened.", Message_Fail);
161     return Handle(Image_PixMap)();
162   }
163   aFile.seekg ((std::streamoff )theOffset, std::ios_base::beg);
164   if (!aFile.good())
165   {
166     Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: Image is defined with invalid file offset '") + thePath + "'.", Message_Fail);
167     return Handle(Image_PixMap)();
168   }
169
170   Handle(Image_AlienPixMap) anImage = new Image_AlienPixMap();
171   if (!anImage->Load (aFile, thePath))
172   {
173     return Handle(Image_PixMap)();
174   }
175   return anImage;
176 }
177
178 // ================================================================
179 // Function : ProbeImageFileFormat
180 // Purpose  :
181 // ================================================================
182 TCollection_AsciiString Image_Texture::ProbeImageFileFormat() const
183 {
184   static const int THE_PROBE_SIZE = 20;
185   char aBuffer[THE_PROBE_SIZE];
186   if (!myBuffer.IsNull())
187   {
188     memcpy (aBuffer, myBuffer->Data(), myBuffer->Size() < THE_PROBE_SIZE ? myBuffer->Size() : THE_PROBE_SIZE);
189   }
190   else
191   {
192     std::ifstream aFileIn;
193     OSD_OpenStream (aFileIn, myImagePath.ToCString(), std::ios::in | std::ios::binary);
194     if (!aFileIn)
195     {
196       Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: Unable to open file ") + myImagePath + "!", Message_Fail);
197       return false;
198     }
199     if (myOffset >= 0)
200     {
201       aFileIn.seekg ((std::streamoff )myOffset, std::ios_base::beg);
202       if (!aFileIn.good())
203       {
204         Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: Image is defined with invalid file offset '") + myImagePath + "'.", Message_Fail);
205         return false;
206       }
207     }
208
209     if (!aFileIn.read (aBuffer, THE_PROBE_SIZE))
210     {
211       Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: unable to read image file '") + myImagePath + "'", Message_Fail);
212       return false;
213     }
214   }
215
216   if (memcmp (aBuffer, "\x89" "PNG\r\n" "\x1A" "\n", 8) == 0)
217   {
218     return "png";
219   }
220   else if (memcmp (aBuffer, "\xFF\xD8\xFF", 3) == 0)
221   {
222     return "jpg";
223   }
224   else if (memcmp (aBuffer, "GIF87a", 6) == 0
225         || memcmp (aBuffer, "GIF89a", 6) == 0)
226   {
227     return "gif";
228   }
229   else if (memcmp (aBuffer, "II\x2A\x00", 4) == 0
230         || memcmp (aBuffer, "MM\x00\x2A", 4) == 0)
231   {
232     return "tiff";
233   }
234   else if (memcmp (aBuffer, "BM", 2) == 0)
235   {
236     return "bmp";
237   }
238   else if (memcmp (aBuffer,     "RIFF", 4) == 0
239         && memcmp (aBuffer + 8, "WEBP", 4) == 0)
240   {
241     return "webp";
242   }
243   return "";
244 }
245
246 // ================================================================
247 // Function : WriteImage
248 // Purpose  :
249 // ================================================================
250 Standard_Boolean Image_Texture::WriteImage (const TCollection_AsciiString& theFile)
251 {
252   Handle(NCollection_Buffer) aBuffer = myBuffer;
253   if (myBuffer.IsNull())
254   {
255     std::ifstream aFileIn;
256     OSD_OpenStream (aFileIn, myImagePath.ToCString(), std::ios::in | std::ios::binary);
257     if (!aFileIn)
258     {
259       Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: Unable to open file ") + myImagePath + "!", Message_Fail);
260       return Standard_False;
261     }
262
263     Standard_Size aLen = (Standard_Size )myLength;
264     if (myOffset >= 0)
265     {
266       aFileIn.seekg ((std::streamoff )myOffset, std::ios_base::beg);
267       if (!aFileIn.good())
268       {
269         Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: Image is defined with invalid file offset '") + myImagePath + "'.", Message_Fail);
270         return Standard_False;
271       }
272     }
273     else
274     {
275       aFileIn.seekg (0, std::ios_base::end);
276       aLen = (Standard_Size )aFileIn.tellg();
277       aFileIn.seekg (0, std::ios_base::beg);
278     }
279
280     aBuffer = new NCollection_Buffer (NCollection_BaseAllocator::CommonBaseAllocator(), aLen);
281     if (!aFileIn.read ((char* )aBuffer->ChangeData(), aBuffer->Size()))
282     {
283       Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: unable to read image file '") + myImagePath + "'", Message_Fail);
284       return Standard_False;
285     }
286   }
287
288   std::ofstream aFileOut;
289   OSD_OpenStream (aFileOut, theFile.ToCString(), std::ios::out | std::ios::binary | std::ios::trunc);
290   if (!aFileOut)
291   {
292     Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: Unable to create file ") + theFile + "!", Message_Fail);
293     return Standard_False;
294   }
295
296   aFileOut.write ((const char* )aBuffer->Data(), aBuffer->Size());
297   aFileOut.close();
298   if (!aFileOut.good())
299   {
300     Message::DefaultMessenger()->Send (TCollection_AsciiString ("Error: Unable to write file ") + theFile + "!", Message_Fail);
301     return Standard_False;
302   }
303   return Standard_True;
304 }