Bug in SetLayerStyle and Brushes?

Report any Hollywood bugs here
Post Reply
nexus
Posts: 150
Joined: Sun Mar 07, 2010 11:54 am

Bug in SetLayerStyle and Brushes?

Post by nexus »

consider the following code snippet:

Code: Select all

@VERSION 8,0
@DISPLAY { Title = "Test", Width=800, Height=600, Mode="Windowed" }
EnableLayers()
EscapeQuit(True)

Function p_HideShow(hLayer,sLayer)
  Local hide = HideLayerFX(hLayer, { Async = True, type = #WATER1 })
  Local show = ShowLayerFX(sLayer, { Async = True, type = #WATER1 })
  Local num = GetAttribute(#ASYNCDRAW, hide, #ATTRNUMFRAMES) 
  
  For Local k = 1 To num 
    AsyncDrawFrame(hide,k)
    AsyncDrawFrame(show,k)
  Next
  AsyncDrawFrame(hide,num)
  AsyncDrawFrame(show,num)

  FinishAsyncDraw(hide)
  FinishAsyncDraw(show)

  ; should also not be necessary but does not help or harm either:
  ;SetLayerStyle(sLayer,{hidden=False})
EndFunction

;4 pictures taken from one of the Hollywood Examples
Local photo1$ = "Hollywood\Examples\Hollywood\AsyncFX\pics\01.jpg"  
Local photo2$ = "Hollywood\Examples\Hollywood\AsyncFX\pics\02.jpg"
Local photo3$ = "Hollywood\Examples\Hollywood\AsyncFX\pics\03.jpg"
Local photo4$ = "Hollywood\Examples\Hollywood\AsyncFX\pics\04.jpg"

LoadBrush(1, photo1$ , { Transparency = #BLACK })
InsertLayer(1, #BRUSH, 1, #CENTER, #CENTER, True)
SetLayerName(1, "Photo1")

LoadBrush(2, photo2$ , { Transparency = #BLACK })
InsertLayer(2, #BRUSH, 2, #CENTER, #CENTER, True)
SetLayerName(2, "Photo2")

p_HideShow("Photo1","Photo2")
;should not be necessary but doesn't help or harm either:
;SwapLayers("Photo1","photo2")

FreeBrush(1)
LoadBrush(1, photo3$ , { Transparency = #BLACK })
SetLayerStyle("Photo1", { ID = 1, hidden=True, x=#CENTER, y=#CENTER })

; FIRST BUTTON CLICK should  hide photo2$ and show photo3$
WaitLeftMouse
p_HideShow("Photo2","Photo1")
;SwapLayers("Photo1","photo2")

FreeBrush(2)

LoadBrush(2, photo4$ , { Transparency = #BLACK })
SetLayerStyle("Photo1", { ID = 2, hidden=True, x=#CENTER, y=#CENTER })

; SECOND BUTTON CLICK should  hide photo3$ and show photo4$
WaitLeftMouse
p_HideShow("Photo1","Photo2")
;SwapLayers("Photo1","photo2")

Repeat
	WaitEvent
Forever
Problem is
1. after FIRST BUTTON CLICK, the window is black, i.e. no photo is shown.
2. after SECOND BUTTON CLICK, the window shortly shows the transition from photo3$ to photo4$ (I think -- its too fast to be sure) but in the end photo2$ is shown again.

I also tried with using for photo3$ and photo4$ the new BRUSH IDs 3 and 4 instead of re-using 1 and 2. This did not change anything.

Do I misunderstand `SetLayerStyle()` method on #BRUSH as said in the docu:
https://www.hollywood-mal.com/docs/html ... Style.html
#BRUSH, #BRUSHPART and #BGPICPART layers recognize the following elements:

ID
This table element can be used to assign a new brush/bgpic to the specified layer. The old brush/bgpic will then be replaced with the new one specified by ID. This is useful for example for a slideshow in which a layer shall display a new picture every n seconds.
To me, it reads that my code above should work as expected and show 4 photos one after another.

I tested it currently with Hollywood 10 on Windows 11.

What's my mistake? Or is it a bug :)

Thanks,
nexus
Flinx
Posts: 255
Joined: Sun Feb 14, 2021 9:54 am
Location: Germany

Re: Bug in SetLayerStyle and Brushes?

Post by Flinx »

Mistake. Only one bad number. The line before "; SECOND BUTTON CLICK..." should not have "Photo1" but "Photo2".
nexus
Posts: 150
Joined: Sun Mar 07, 2010 11:54 am

Re: Bug in SetLayerStyle and Brushes?

Post by nexus »

You are right, of course. It means, that example does not show the "error" I wanted to show. Need to be more careful when constructing an example and study my actual problem again. But maybe the real error is as simple to fix as this one here :D.

Anyway, thanks for your reply! Well appreciated :)

Tom.
nexus
Posts: 150
Joined: Sun Mar 07, 2010 11:54 am

Re: Bug in SetLayerStyle and Brushes?

Post by nexus »

Alright, second try.

Almost the same code as above but with the fix from @Flinx + skipping some frames of the show layer:

Code: Select all

 Local bigger_k = k * 2
 bigger_k = IIf(bigger_k <= num, bigger_k, num)
(+ FX is now #STRUDEL + Wait(1) in Loop .. just to be able to see something). See code:

Code: Select all

@VERSION 8,0
@DISPLAY { Title = "Test", Width=800, Height=600, Mode="Windowed" }
EnableLayers()
EscapeQuit(True)

Function p_HideShow(hLayer,sLayer)
  Local hide = HideLayerFX(hLayer, { Async = True, type = #STRUDEL })
  Local show = ShowLayerFX(sLayer, { Async = True, type = #STRUDEL })
  Local num = GetAttribute(#ASYNCDRAW, hide, #ATTRNUMFRAMES) 
  
  For Local k = 1 To num 
    AsyncDrawFrame(hide,k)
    Local bigger_k = k * 2
    bigger_k = IIf(bigger_k <= num, bigger_k, num)
    AsyncDrawFrame(show,bigger_k)
    Wait(1)
  Next
  AsyncDrawFrame(hide,num)
  AsyncDrawFrame(show,num)

  FinishAsyncDraw(hide)
  FinishAsyncDraw(show)

EndFunction

Local photo1$ = "D:\\Users\\nexus\\Downloads\\hollywood\\test\\pics_small\\01.jpg"  
Local photo2$ = "D:\\Users\\nexus\\Downloads\\hollywood\\test\\pics_small\\02.jpg"
Local photo3$ = "D:\\Users\\nexus\\Downloads\\hollywood\\test\\pics_small\\03.jpg"
Local photo4$ = "D:\\Users\\nexus\\Downloads\\hollywood\\test\\pics_small\\04.jpg"

LoadBrush(1, photo1$ , { Transparency = #BLACK })
InsertLayer(1, #BRUSH, 1, #CENTER, #CENTER, True)
SetLayerName(1, "Photo1")

LoadBrush(2, photo2$ , { Transparency = #BLACK })
InsertLayer(2, #BRUSH, 2, #CENTER, #CENTER, True)
SetLayerName(2, "Photo2")

p_HideShow("Photo1","Photo2")

FreeBrush(1)
LoadBrush(1, photo3$ , { Transparency = #BLACK })
SetLayerStyle("Photo1", { ID = 1, hidden=True, x=#CENTER, y=#CENTER })

WaitLeftMouse
p_HideShow("Photo2","Photo1")

FreeBrush(2)
LoadBrush(2, photo4$ , { Transparency = #BLACK })
SetLayerStyle("Photo2", { ID = 2, hidden=True, x=#CENTER, y=#CENTER })

WaitLeftMouse
p_HideShow("Photo1","Photo2")

Repeat
	WaitEvent
Forever
Result:
The respective photos `photo$1`, `photo$2`, `photo$3` are correctly hidden by `HideLayerFX()`,
but photos `photo$2`, `photo$3`, `photo$4` are not shown by `ShowLayerFX()`.

I realized that the issue can be partly fixed by changing the line

Code: Select all

bigger_k = IIf(bigger_k <= num, bigger_k, num)
to

Code: Select all

bigger_k = IIf(bigger_k <= num, bigger_k, num-1)
In this case, however, only the last frame is shown at the end of the loop. Can be seen when placing a `;` in front of `AsyncDrawFrame(hide,k)` to not show the FX for hiding. The behavior seems to be wrong in both cases. The FX has 90 frames to draw and I would expect to see all frames 2,4,6,8 up to 90.

Now after re-checking the docu again, it might be related to:
Frame seeking does only work with asynchronous drawing objects of type #ADF_FX
But I cannot really believe that because in my actual application it works most of the time. Just sometimes, a chosen async FX results in a black screen. In the code above, it works, too, when choosing an offset `+2` instead of `*2`.

Another thing to mention: The docu says:
If you manually specify the frame to draw, you also need to pay attention that your asynchronous drawing object is freed correctly. If you do not use the optional argument, the async drawing object is automatically freed when AsyncDrawFrame() returns True. If you specify a frame manually, the async draw object is never freed. Even if you specify the last frame, Hollywood will not free the async draw object
That seems not to be true. The async drawing object seems to be freed when specifying a frame number that is higher than the number of available frames (in the code above: `num+1`). At least `FinishAsyncDraw()`gives me an error afterwards:
Requested object not found!
File: test.hws (current line:24 - In function: FinishAsyncDraw)
nexus
Posts: 150
Joined: Sun Mar 07, 2010 11:54 am

Re: Bug in SetLayerStyle and Brushes?

Post by nexus »

One more finding from the docu:
https://hollywood-mal.com/docs/html/hol ... ibute.html
#ATTRNUMFRAMES:
Returns the number of frames of this asynchronous drawing object; please note that if you use this value as the base for a loop over AsyncDrawFrame(), you must add one loop because the final call to AsyncDrawFrame() does not count as a frame; See AsyncDrawFrame for details.
Does it meant you have to run the loop from 1 To num+1?

At least it does not seem to match that part of the docu:
https://hollywood-mal.com/docs/html/hol ... Frame.html
To find out the number of frames of an asynchronous draw object, you have to call GetAttribute() on it using #ATTRNUMFRAMES. The value you get from this call is the largest valid frame number.
nexus
Posts: 150
Joined: Sun Mar 07, 2010 11:54 am

Re: Bug in SetLayerStyle and Brushes?

Post by nexus »

The following is pure speculation but from my "real" application it seems, the problem occurs only with async-hiding/showing 2 layers at the same time + skipping some frames in the loop, if the number of frames to hide is bigger than the number of frames to show. Even if u re-draw the same last frame of the "toShow"-layer, that seems to be the condition (maybe Hollywood is caching such situation? :)

If the situation is the other way around, i.e. number of frames to show is higher than number of frames to hide, than hiding/showing 2 layers at the same time seems to work fine.

:)

Maybe that helps @Andreas :D

cheers,
Tom
User avatar
airsoftsoftwair
Posts: 5615
Joined: Fri Feb 12, 2010 2:33 pm
Location: Germany
Contact:

Re: Bug in SetLayerStyle and Brushes?

Post by airsoftsoftwair »

nexus wrote: Tue May 21, 2024 7:57 pm In this case, however, only the last frame is shown at the end of the loop. Can be seen when placing a `;` in front of `AsyncDrawFrame(hide,k)` to not show the FX for hiding. The behavior seems to be wrong in both cases. The FX has 90 frames to draw and I would expect to see all frames 2,4,6,8 up to 90.
Yes, clearly a bug. Seems to be related to #STRUDEL but maybe there are other effects that are affected. When using #REVEALLEFT instead of #STRUDEL, it works correctly, though.
nexus wrote: Tue May 21, 2024 7:57 pm Now after re-checking the docu again, it might be related to: "Frame seeking does only work with asynchronous drawing objects of type #ADF_FX"
But I cannot really believe that because in my actual application it works most of the time.
Of course it does, because if you use asynchronous object effects they'll be of the type #ADF_FX, so no contradiction here.
nexus wrote: Tue May 21, 2024 7:57 pm Another thing to mention: The docu says: "If you manually specify the frame to draw, you also need to pay attention that your asynchronous drawing object is freed correctly. If you do not use the optional argument, the async drawing object is automatically freed when AsyncDrawFrame() returns True. If you specify a frame manually, the async draw object is never freed. Even if you specify the last frame, Hollywood will not free the async draw object" That seems not to be true. The async drawing object seems to be freed when specifying a frame number that is higher than the number of available frames (in the code above: `num+1`). At least `FinishAsyncDraw()`gives me an error afterwards:
Yes, that is true. It's an undocumented feature. Normally AsyncDrawFrame() will NOT accept a frame number that is greater than what is returned by #ATTRNUMFRAMES. There's only one exception: If you first pass the last frame (i.e. the frame count returned by #ATTRNUMFRAMES) to AsyncDrawFrame(), and after that you call it again with a frame index greater than the frame count returned by #ATTRNUMFRAMES AsyncDrawFrame() will indeed free the object, contrary to what is said in the documentation. But that's a very special case and I think it's too confusing to document that very behaviour so let's just keep the documentation like it is because it's getting too confusing otherwise.
nexus wrote: Wed May 22, 2024 8:30 am One more finding from the docu:
#ATTRNUMFRAMES:
Returns the number of frames of this asynchronous drawing object; please note that if you use this value as the base for a loop over AsyncDrawFrame(), you must add one loop because the final call to AsyncDrawFrame() does not count as a frame; See AsyncDrawFrame for details.

Does it meant you have to run the loop from 1 To num+1?
You have two options:

1. Either run the loop from frames 1 to num+1. In that case, the last loop iteration will free the async draw object.
2. Or run the loop from 1 to num. In that case, you'll have to call FinishAsyncDraw() to manually free the async draw object.

I agree that the fact that the documentation of #ATTRNUMFRAMES says that you need to call AsyncDrawFrame() one more time is a little confusing. The reason for that is probably that FinishAsyncDraw(), as the documentation says, was originally designed to skip to the end of the effect and free it. So if you draw all frames anyway, you don't really have to skip to the end of the effect which is why you shouldn't call FinishAsyncDraw() but just keep on calling AsyncDrawFrame() and let that free the effect. But as described above, it is also possible to call FinishAsyncDraw() but then you must only iterate until #ATTRNUMFRAMES, not #ATTRNUMFRAMES+1.

If you're skipping frames, however, it will become more complicated because AsyncDrawFrame() will only accept a frame at index #ATTRNUMFRAMES+1 if it has drawn the frame at index #ATTRNUMFRAMES right before that. Hmm, so maybe it's easier to use FinishAsyncDraw() nevertheless ;)
The following is pure speculation but from my "real" application it seems, the problem occurs only with async-hiding/showing 2 layers at the same time + skipping some frames in the loop, if the number of frames to hide is bigger than the number of frames to show. Even if u re-draw the same last frame of the "toShow"-layer, that seems to be the condition (maybe Hollywood is caching such situation?
No, some effects don't seem to be able to handle seeking correctly. It works fine with #REVEALLEFT but not with #STRUDEL. I need to investigate into it.
User avatar
airsoftsoftwair
Posts: 5615
Joined: Fri Feb 12, 2010 2:33 pm
Location: Germany
Contact:

Re: Bug in SetLayerStyle and Brushes?

Post by airsoftsoftwair »

Code: Select all

- Fix: Using AsyncDrawFrame() to draw non-consecutive, sparse frames of a transition effect started using
  ShowLayerFX() kept the layer hidden for several effects like #STRUDEL et al.
There's also a simple workaround: Just call ShowLayer() manually and then call AsyncDrawFrame() as often as you wish...
Post Reply