1 // Copyright (c) 2019 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 <OpenGl_AspectsSprite.hxx>
15 
16 #include <OpenGl_Context.hxx>
17 #include <OpenGl_PointSprite.hxx>
18 #include <OpenGl_TextureSet.hxx>
19 
20 #include <Image_PixMap.hxx>
21 #include <Graphic3d_MarkerImage.hxx>
22 #include <TColStd_HArray1OfByte.hxx>
23 
24 namespace
25 {
26   static const TCollection_AsciiString THE_EMPTY_KEY;
27 }
28 
29 // =======================================================================
30 // function : Release
31 // purpose  :
32 // =======================================================================
Release(OpenGl_Context * theCtx)33 void OpenGl_AspectsSprite::Release (OpenGl_Context* theCtx)
34 {
35   myIsSpriteReady = Standard_False;
36   if (mySprite.IsNull())
37   {
38     return;
39   }
40 
41   if (theCtx != NULL)
42   {
43     if (mySprite->ResourceId().IsEmpty())
44     {
45       theCtx->DelayedRelease (mySprite);
46       theCtx->DelayedRelease (mySpriteA);
47     }
48     else
49     {
50       {
51         const TCollection_AsciiString aSpriteKey = mySprite->ResourceId();
52         mySprite.Nullify(); // we need nullify all handles before ReleaseResource() call
53         theCtx->ReleaseResource (aSpriteKey,  Standard_True);
54       }
55       if (!mySpriteA.IsNull())
56       {
57         const TCollection_AsciiString aSpriteKeyA = mySpriteA->ResourceId();
58         mySpriteA.Nullify();
59         theCtx->ReleaseResource (aSpriteKeyA, Standard_True);
60       }
61     }
62   }
63   mySprite.Nullify();
64   mySpriteA.Nullify();
65 }
66 
67 // =======================================================================
68 // function : HasPointSprite
69 // purpose  :
70 // =======================================================================
HasPointSprite(const Handle (OpenGl_Context)& theCtx,const Handle (Graphic3d_Aspects)& theAspects)71 bool OpenGl_AspectsSprite::HasPointSprite (const Handle(OpenGl_Context)& theCtx,
72                                            const Handle(Graphic3d_Aspects)& theAspects)
73 {
74   const Handle(OpenGl_PointSprite)& aSprite = Sprite (theCtx, theAspects, false);
75   return !aSprite.IsNull()
76       && !aSprite->IsDisplayList();
77 }
78 
79 // =======================================================================
80 // function : IsDisplayListSprite
81 // purpose  :
82 // =======================================================================
IsDisplayListSprite(const Handle (OpenGl_Context)& theCtx,const Handle (Graphic3d_Aspects)& theAspects)83 bool OpenGl_AspectsSprite::IsDisplayListSprite (const Handle(OpenGl_Context)& theCtx,
84                                                 const Handle(Graphic3d_Aspects)& theAspects)
85 {
86 #if !defined(GL_ES_VERSION_2_0)
87   const Handle(OpenGl_PointSprite)& aSprite = Sprite (theCtx, theAspects, false);
88   return !aSprite.IsNull()
89        && aSprite->IsDisplayList();
90 #else
91   (void )theCtx;
92   (void )theAspects;
93   return false;
94 #endif
95 }
96 
97 // =======================================================================
98 // function : UpdateRediness
99 // purpose  :
100 // =======================================================================
UpdateRediness(const Handle (Graphic3d_Aspects)& theAspect)101 void OpenGl_AspectsSprite::UpdateRediness (const Handle(Graphic3d_Aspects)& theAspect)
102 {
103   // update sprite resource bindings
104   TCollection_AsciiString aSpriteKeyNew, aSpriteAKeyNew;
105   spriteKeys (theAspect->MarkerImage(), theAspect->MarkerType(), theAspect->MarkerScale(), theAspect->ColorRGBA(), aSpriteKeyNew, aSpriteAKeyNew);
106   const TCollection_AsciiString& aSpriteKeyOld  = !mySprite.IsNull()  ? mySprite ->ResourceId() : THE_EMPTY_KEY;
107   const TCollection_AsciiString& aSpriteAKeyOld = !mySpriteA.IsNull() ? mySpriteA->ResourceId() : THE_EMPTY_KEY;
108   if (aSpriteKeyNew.IsEmpty()  || aSpriteKeyOld  != aSpriteKeyNew
109    || aSpriteAKeyNew.IsEmpty() || aSpriteAKeyOld != aSpriteAKeyNew)
110   {
111     myIsSpriteReady = Standard_False;
112     myMarkerSize = theAspect->MarkerScale();
113   }
114 }
115 
116 // =======================================================================
117 // function : Sprite
118 // purpose  :
119 // =======================================================================
Handle(OpenGl_PointSprite)120 const Handle(OpenGl_PointSprite)& OpenGl_AspectsSprite::Sprite (const Handle(OpenGl_Context)& theCtx,
121                                                                 const Handle(Graphic3d_Aspects)& theAspects,
122                                                                 bool theIsAlphaSprite)
123 {
124   if (!myIsSpriteReady)
125   {
126     build (theCtx, theAspects->MarkerImage(), theAspects->MarkerType(), theAspects->MarkerScale(), theAspects->ColorRGBA(), myMarkerSize);
127     myIsSpriteReady = true;
128   }
129   return theIsAlphaSprite && !mySpriteA.IsNull() && mySpriteA->IsValid()
130        ? mySpriteA
131        : mySprite;
132 }
133 
134 // =======================================================================
135 // function : build
136 // purpose  :
137 // =======================================================================
build(const Handle (OpenGl_Context)& theCtx,const Handle (Graphic3d_MarkerImage)& theMarkerImage,Aspect_TypeOfMarker theType,Standard_ShortReal theScale,const Graphic3d_Vec4 & theColor,Standard_ShortReal & theMarkerSize)138 void OpenGl_AspectsSprite::build (const Handle(OpenGl_Context)& theCtx,
139                                   const Handle(Graphic3d_MarkerImage)& theMarkerImage,
140                                   Aspect_TypeOfMarker theType,
141                                   Standard_ShortReal theScale,
142                                   const Graphic3d_Vec4& theColor,
143                                   Standard_ShortReal& theMarkerSize)
144 {
145   // generate key for shared resource
146   TCollection_AsciiString aNewKey, aNewKeyA;
147   spriteKeys (theMarkerImage, theType, theScale, theColor, aNewKey, aNewKeyA);
148 
149   const TCollection_AsciiString& aSpriteKeyOld  = !mySprite.IsNull()  ? mySprite ->ResourceId() : THE_EMPTY_KEY;
150   const TCollection_AsciiString& aSpriteAKeyOld = !mySpriteA.IsNull() ? mySpriteA->ResourceId() : THE_EMPTY_KEY;
151 
152   // release old shared resources
153   const Standard_Boolean aNewResource = aNewKey.IsEmpty()
154                                      || aSpriteKeyOld != aNewKey;
155   if (aNewResource)
156   {
157     if (!mySprite.IsNull())
158     {
159       if (mySprite->ResourceId().IsEmpty())
160       {
161         theCtx->DelayedRelease (mySprite);
162         mySprite.Nullify();
163       }
164       else
165       {
166         const TCollection_AsciiString anOldKey = mySprite->ResourceId();
167         mySprite.Nullify(); // we need nullify all handles before ReleaseResource() call
168         theCtx->ReleaseResource (anOldKey, Standard_True);
169       }
170     }
171   }
172   if (aNewKeyA.IsEmpty() || aSpriteAKeyOld != aNewKeyA)
173   {
174     if (!mySpriteA.IsNull())
175     {
176       if (mySpriteA->ResourceId().IsEmpty())
177       {
178         theCtx->DelayedRelease (mySpriteA);
179         mySpriteA.Nullify();
180       }
181       else
182       {
183         const TCollection_AsciiString anOldKey = mySpriteA->ResourceId();
184         mySpriteA.Nullify(); // we need nullify all handles before ReleaseResource() call
185         theCtx->ReleaseResource (anOldKey, Standard_True);
186       }
187     }
188   }
189 
190   if (!aNewResource)
191   {
192     const OpenGl_PointSprite* aSprite = dynamic_cast<OpenGl_PointSprite*> (mySprite.get());
193     if (!aSprite->IsDisplayList())
194     {
195       theMarkerSize = Standard_ShortReal (Max (aSprite->SizeX(), aSprite->SizeY()));
196     }
197     return;
198   }
199   if (theType == Aspect_TOM_POINT
200    || theType == Aspect_TOM_EMPTY
201    || (theType == Aspect_TOM_USERDEFINED && theMarkerImage.IsNull()))
202   {
203     // nothing to do - just simple point
204     return;
205   }
206 
207   Handle(OpenGl_PointSprite)& aSprite  = mySprite;
208   Handle(OpenGl_PointSprite)& aSpriteA = mySpriteA;
209   if (!aNewKey.IsEmpty())
210   {
211     theCtx->GetResource<Handle(OpenGl_PointSprite)> (aNewKeyA, aSpriteA); // alpha sprite could be shared
212     theCtx->GetResource<Handle(OpenGl_PointSprite)> (aNewKey,  aSprite);
213   }
214 
215   const bool hadAlreadyRGBA  = !aSprite.IsNull();
216   const bool hadAlreadyAlpha = !aSpriteA.IsNull();
217   if (hadAlreadyRGBA
218    && hadAlreadyAlpha)
219   {
220     // reuse shared resource
221     if (!aSprite->IsDisplayList())
222     {
223       theMarkerSize = Standard_ShortReal (Max (aSprite->SizeX(), aSprite->SizeY()));
224     }
225     return;
226   }
227 
228   if (!hadAlreadyAlpha)
229   {
230     aSpriteA = new OpenGl_PointSprite (aNewKeyA);
231   }
232   if (!hadAlreadyRGBA)
233   {
234     aSprite = new OpenGl_PointSprite (aNewKey);
235   }
236   if (!aNewKey.IsEmpty())
237   {
238     if (!hadAlreadyRGBA)
239     {
240       theCtx->ShareResource (aNewKey, aSprite);
241     }
242     if (!hadAlreadyAlpha)
243     {
244       theCtx->ShareResource (aNewKeyA, aSpriteA);
245     }
246   }
247 
248   Handle(Graphic3d_MarkerImage) aNewMarkerImage = theType == Aspect_TOM_USERDEFINED && !theMarkerImage.IsNull()
249                                                 ? theMarkerImage
250                                                 : Graphic3d_MarkerImage::StandardMarker (theType, theScale, theColor);
251   if (aNewMarkerImage.IsNull())
252   {
253     return;
254   }
255 
256   if (theCtx->core20fwd != NULL
257    && (!theCtx->caps->pntSpritesDisable || theCtx->core11ffp == NULL))
258   {
259     // Creating texture resource for using it with point sprites
260     Handle(Image_PixMap) anImage = aNewMarkerImage->GetImage();
261     theMarkerSize = Max ((Standard_ShortReal )anImage->Width(),(Standard_ShortReal )anImage->Height());
262 
263     if (!hadAlreadyRGBA)
264     {
265       aSprite->Init (theCtx, *anImage, Graphic3d_TOT_2D, true);
266     }
267     if (!hadAlreadyAlpha)
268     {
269       if (Handle(Image_PixMap) anImageA = aSprite->GetFormat() != GL_ALPHA ? aNewMarkerImage->GetImageAlpha() : Handle(Image_PixMap)())
270       {
271         aSpriteA->Init (theCtx, *anImageA, Graphic3d_TOT_2D, true);
272       }
273     }
274   }
275   else if (theCtx->core11ffp != NULL)
276   {
277   #if !defined(GL_ES_VERSION_2_0)
278     // Creating list with bitmap for using it in compatibility mode
279     GLuint aBitmapList = theCtx->core11ffp->glGenLists (1);
280     aSprite->SetDisplayList (theCtx, aBitmapList);
281 
282     Handle(Image_PixMap) anImage = aNewMarkerImage->IsColoredImage()
283                                  ? aNewMarkerImage->GetImage()
284                                  : Handle(Image_PixMap)();
285     const OpenGl_TextureFormat aFormat = !anImage.IsNull()
286                                        ? OpenGl_TextureFormat::FindFormat (theCtx, anImage->Format(), true)
287                                        : OpenGl_TextureFormat();
288     if (aFormat.IsValid())
289     {
290       if (anImage->IsTopDown())
291       {
292         Handle(Image_PixMap) anImageCopy = new Image_PixMap();
293         anImageCopy->InitCopy (*anImage);
294         Image_PixMap::FlipY (*anImageCopy);
295         anImage = anImageCopy;
296       }
297       const GLint anAligment = Min ((GLint)anImage->MaxRowAligmentBytes(), 8);
298       theCtx->core11fwd->glPixelStorei (GL_UNPACK_ALIGNMENT, anAligment);
299 
300       const GLint anExtraBytes = GLint (anImage->RowExtraBytes());
301       const GLint aPixelsWidth = GLint (anImage->SizeRowBytes() / anImage->SizePixelBytes());
302       const GLint aRowLength = (anExtraBytes >= anAligment) ? aPixelsWidth : 0;
303       theCtx->core11fwd->glPixelStorei (GL_UNPACK_ROW_LENGTH, aRowLength);
304 
305       theCtx->core11ffp->glNewList (aBitmapList, GL_COMPILE);
306       const Standard_Integer aWidth = (Standard_Integer )anImage->Width(), aHeight = (Standard_Integer )anImage->Height();
307       theCtx->core11ffp->glBitmap (0, 0, 0, 0, GLfloat(-0.5f * aWidth), GLfloat(-0.5f * aHeight), NULL); // make offsets that will be added to the current raster position
308       theCtx->core11ffp->glDrawPixels (GLsizei(anImage->Width()), GLsizei(anImage->Height()), aFormat.PixelFormat(), aFormat.DataType(), anImage->Data());
309       theCtx->core11ffp->glEndList();
310       theCtx->core11fwd->glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
311       theCtx->core11fwd->glPixelStorei (GL_UNPACK_ROW_LENGTH, 0);
312     }
313 
314     if (!aFormat.IsValid() || !hadAlreadyAlpha)
315     {
316       if (aFormat.IsValid())
317       {
318         aBitmapList = glGenLists (1);
319         aSpriteA->SetDisplayList (theCtx, aBitmapList);
320       }
321 
322       Standard_Integer aWidth = 0, aHeight = 0;
323       aNewMarkerImage->GetTextureSize (aWidth, aHeight);
324       if (Handle(TColStd_HArray1OfByte) aBitMap = aNewMarkerImage->GetBitMapArray())
325       {
326         theCtx->core11ffp->glNewList (aBitmapList, GL_COMPILE);
327         theCtx->core11ffp->glBitmap ((GLsizei)aWidth, (GLsizei)aHeight, (GLfloat)(0.5f * aWidth), (GLfloat)(0.5f * aHeight),
328                                      0.f, 0.f, (const GLubyte*)&aBitMap->First());
329         theCtx->core11ffp->glEndList();
330       }
331     }
332   #endif
333   }
334 }
335 // =======================================================================
336 // function : spriteKeys
337 // purpose  :
338 // =======================================================================
spriteKeys(const Handle (Graphic3d_MarkerImage)& theMarkerImage,Aspect_TypeOfMarker theType,Standard_ShortReal theScale,const Graphic3d_Vec4 & theColor,TCollection_AsciiString & theKey,TCollection_AsciiString & theKeyA)339 void OpenGl_AspectsSprite::spriteKeys (const Handle(Graphic3d_MarkerImage)& theMarkerImage,
340                                        Aspect_TypeOfMarker theType,
341                                        Standard_ShortReal theScale,
342                                        const Graphic3d_Vec4& theColor,
343                                        TCollection_AsciiString& theKey,
344                                        TCollection_AsciiString& theKeyA)
345 {
346   // generate key for shared resource
347   if (theType == Aspect_TOM_USERDEFINED)
348   {
349     if (!theMarkerImage.IsNull())
350     {
351       theKey  = theMarkerImage->GetImageId();
352       theKeyA = theMarkerImage->GetImageAlphaId();
353     }
354   }
355   else if (theType != Aspect_TOM_POINT
356         && theType != Aspect_TOM_EMPTY)
357   {
358     // predefined markers are defined with 0.5 step
359     const Standard_Integer aScale = Standard_Integer(theScale * 10.0f + 0.5f);
360     theKey  = TCollection_AsciiString ("OpenGl_AspectMarker") + theType + "_" + aScale;
361     theKeyA = theKey + "A";
362     if (theType == Aspect_TOM_BALL)
363     {
364       unsigned int aColor[3] =
365       {
366         (unsigned int )(255.0f * theColor.r()),
367         (unsigned int )(255.0f * theColor.g()),
368         (unsigned int )(255.0f * theColor.b())
369       };
370       char aBytes[8];
371       sprintf (aBytes, "%02X%02X%02X", aColor[0], aColor[1], aColor[2]);
372       theKey += aBytes;
373     }
374   }
375 }
376