1 //
2 // Copyright (c) 2010-2022 Antmicro
3 // Copyright (c) 2011-2015 Realtime Embedded
4 //
5 // This file is licensed under the MIT License.
6 // Full license text is available in 'licenses/MIT.txt'.
7 //
8 using System;
9 using Antmicro.Migrant;
10 using Antmicro.Renode.Backends.Display;
11 using Antmicro.Renode.Core;
12 using Antmicro.Renode.Exceptions;
13 using Antmicro.Renode.UserInterface;
14 using Antmicro.Renode.Utilities;
15 
16 namespace Antmicro.Renode.Peripherals.Video
17 {
18     [Icon("lcd")]
19     public abstract class AutoRepaintingVideo : IVideo, IDisposable
20     {
AutoRepaintingVideo(IMachine machine)21         protected AutoRepaintingVideo(IMachine machine)
22         {
23             innerLock = new object();
24             // we use synchronized thread since some deriving classes can generate interrupt on repainting
25             this.machine = machine;
26             repainter = machine.ObtainManagedThread(DoRepaint, FramesPerVirtualSecond);
27             Endianess = ELFSharp.ELF.Endianess.LittleEndian;
28         }
29 
TakeScreenshot()30         public RawImageData TakeScreenshot()
31         {
32             if(buffer == null)
33             {
34                 throw new RecoverableException("Frame buffer is empty.");
35             }
36 
37             var converter = PixelManipulationTools.GetConverter(Format, Endianess, RawImageData.PixelFormat, ELFSharp.ELF.Endianess.BigEndian);
38             var outBuffer = new byte[Width * Height * RawImageData.PixelFormat.GetColorDepth()];
39             converter.Convert(buffer, ref outBuffer);
40 
41             return new RawImageData(outBuffer, Width, Height);
42         }
43 
Dispose()44         public void Dispose()
45         {
46             repainter.Dispose();
47         }
48 
49         [field: Transient]
50         public event Action<byte[]> FrameRendered;
51 
52         public uint FramesPerVirtualSecond
53         {
54             get
55             {
56                 return framesPerVirtualSecond;
57             }
58             set
59             {
60                 repainter.Dispose();
61                 repainter = machine.ObtainManagedThread(DoRepaint, value);
62                 if(RepainterIsRunning)
63                 {
64                     repainter.Start();
65                 }
66 
67                 framesPerVirtualSecond = value;
68             }
69         }
70         public int Width { get; private set; }
71         public int Height { get; private set; }
72         public PixelFormat Format { get; private set; }
73         public ELFSharp.ELF.Endianess Endianess { get; protected set; }
74         public bool RepainterIsRunning { get; private set; }
75 
76         public event Action<int, int, PixelFormat, ELFSharp.ELF.Endianess> ConfigurationChanged
77         {
78             add
79             {
80                 lock (innerLock)
81                 {
82                     configurationChanged += value;
83                     value(Width, Height, Format, Endianess);
84                 }
85             }
86 
87             remove
88             {
89                 configurationChanged -= value;
90             }
91         }
92 
Reset()93         public abstract void Reset();
94 
Reconfigure(int? width = null, int? height = null, PixelFormat? format = null, bool autoRepaint = true)95         protected void Reconfigure(int? width = null, int? height = null, PixelFormat? format = null, bool autoRepaint = true)
96         {
97             lock(innerLock)
98             {
99                 var flag = false;
100                 if(width != null && Width != width.Value)
101                 {
102                     Width = width.Value;
103                     flag = true;
104                 }
105 
106                 if(height != null && Height != height.Value)
107                 {
108                     Height = height.Value;
109                     flag = true;
110                 }
111 
112                 if(format != null && Format != format.Value)
113                 {
114                     Format = format.Value;
115                     flag = true;
116                 }
117 
118                 if(flag && Width > 0 && Height > 0)
119                 {
120                     buffer = new byte[Width * Height * Format.GetColorDepth()];
121 
122                     var cc = configurationChanged;
123                     if(cc != null)
124                     {
125                         cc(Width, Height, Format, Endianess);
126                     }
127                     if(autoRepaint)
128                     {
129                         RepainterIsRunning = true;
130                         repainter.Start();
131                     }
132                     else
133                     {
134                         RepainterIsRunning = false;
135                         repainter.Stop();
136                     }
137                 }
138                 else
139                 {
140                     RepainterIsRunning = false;
141                     repainter.Stop();
142                 }
143             }
144         }
145 
Repaint()146         protected abstract void Repaint();
147 
DoRepaint()148         protected void DoRepaint()
149         {
150             if(buffer != null)
151             {
152                 Repaint();
153                 var fr = FrameRendered;
154                 if(fr != null)
155                 {
156                     lock(innerLock)
157                     {
158                         fr(buffer);
159                     }
160                 }
161             }
162         }
163 
164         protected byte[] buffer;
165 
166         private IManagedThread repainter;
167         [Transient]
168         private Action<int, int, PixelFormat, ELFSharp.ELF.Endianess> configurationChanged;
169         private readonly object innerLock;
170         private readonly IMachine machine;
171         private uint framesPerVirtualSecond = 25;
172     }
173 }
174 
175