1 /*
2  * Copyright (C) 2015-2017, by Laszlo Szeremi under the Boost license.
3  *
4  * Pixel Perfect Engine, graphics.bitmap module
5  */
6 module pixelperfectengine.graphics.raster;
7 
8 import pixelperfectengine.graphics.outputscreen;
9 import pixelperfectengine.graphics.layers;
10 import pixelperfectengine.graphics.bitmap;
11 import bindbc.sdl;
12 public import pixelperfectengine.graphics.common;
13 import std.conv;
14 import std.algorithm.sorting;
15 import std.algorithm.mutation;
16 import core.time;
17 import collections.treemap;
18 
19 ///The raster calls it every time it finishes the drawing to the framebuffers.
20 ///Used to signal the output screen to switch out the framebuffers.
21 public interface RefreshListener{
22     public void refreshFinished();
23 }
24 
25 ///Used to read the output from the raster and show it on the screen.
26 public interface IRaster{
27     public SDL_Texture* getOutput();
28 }
29 /**
30  * Defines palette handling functions.
31  * It provides various functions for safe palette handling.
32  */
33 public interface PaletteContainer {
34 	/**
35 	 * Returns the palette of the object.
36 	 */
37 	public @property Color[] palette() @safe pure nothrow @nogc;
38 	///Returns the given palette index.
39 	public Color getPaletteIndex(ushort index) @safe pure nothrow @nogc const;
40 	///Sets the given palette index to the given value.
41 	public Color setPaletteIndex(ushort index, Color value) @safe pure nothrow @nogc;
42 	/**
43 	 * Adds a palette chunk to the end of the main palette.
44 	 */
45 	public Color[] addPaletteChunk(Color[] paletteChunk) @safe;
46 	/**
47 	 * Loads a palette into the object.
48 	 * Returns the new palette of the object.
49 	 */
50 	public Color[] loadPalette(Color[] palette) @safe;
51 	/**
52 	 * Loads a palette chunk into the object.
53 	 * The `offset` determines where the palette should be loaded.
54 	 * If it points to an existing place, the indices after that will be overwritten until the whole palette will be copied.
55 	 * If it points to the end or after it, then the palette will be made longer, and will pad with values #00000000 if needed.
56 	 * Returns the new palette of the object.
57 	 */
58 	public Color[] loadPaletteChunk(Color[] paletteChunk, ushort offset) @safe;
59 	/**
60 	 * Clears an area of the palette with zeroes.
61 	 * Returns the original area.
62 	 */
63 	public Color[] clearPaletteChunk(ushort lenght, ushort offset) @safe;
64 }
65 
66 ///Handles multiple layers onto one framebuffer.
67 public class Raster : IRaster, PaletteContainer{
68     private ushort rX, rY;		///Stores screen resolution. Set overscan resolutions at OutputWindow
69     //public SDL_Surface* workpad;
70 	public SDL_Texture*[] frameBuffer;
71 	public void* fbData;
72 	public int fbPitch;
73 	/**
74 	 * Color format is ARGB, with each index having their own transparency.
75 	 */
76     protected Color[] _palette;
77 	alias LayerMap = TreeMap!(int, Layer);
78 	///Stores the layers by their priorities.
79 	public LayerMap layerMap;
80     //private Layer[int] layerList;	
81 	private int[] threads;			///Thread IDs (currently unused).
82     private bool r;					///Set to true if refresh is happening.
83 	protected ubyte nOfBuffers;		///Number of framebuffers, 2 for double buffering.
84 	protected ubyte updatedBuffer;	///Framebuffer currently being updated
85 	protected ubyte displayedBuffer;///Framebuffer currently being displayed
86 	//private int[2] doubleBufferRegisters;
87     private RefreshListener[] rL;				///Contains RefreshListeners associated with this raster.
88 	private MonoTime frameTime, frameTime_1;	///Timestamps of frame occurences
89 	private Duration delta_frameTime;			///Current time delta between two frames
90 	private real framesPerSecond, avgFPS;		///Current and average fps counter
91 	//public Bitmap16Bit[2] frameBuffer;
92 
93 	///Creates a raster with the supplied parameters.
94 	///Params:
95 	///   x = Width of the raster.
96 	///   y = Height of the raster.
97 	///   paletteLength = The initial size of the palette.
98 	///   buffers = The number of buffers to be used. Default is two for double buffering. Single buffering can
99 	///eliminate occassional flickering and latency at the possibility of screen tearing and having to wait for
100 	///the buffer to be finished.
101     public this(ushort x, ushort y, OutputScreen oW, size_t paletteLength, ubyte buffers = 2){
102         //workpad = SDL_CreateRGBSurface(SDL_SWSURFACE, x, y, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
103 		//this.threads = threads;
104 		assert(paletteLength <= 65_536);
105 		_palette.length = paletteLength;
106 		SDL_Renderer* renderer = oW.renderer;
107         rX=x;
108         rY=y;
109 		nOfBuffers = buffers;
110 		for (int i ; i < buffers ; i++)
111 			frameBuffer ~= SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGRX8888, SDL_TEXTUREACCESS_STREAMING, x, y);
112 		/+doubleBufferRegisters[0] = 1;
113 		doubleBufferRegisters[1] = 0;+/
114 		oW.setMainRaster(this);
115 		addRefreshListener(oW);
116 		frameTime = MonoTimeImpl!(ClockType.normal).currTime();
117 		framesPerSecond = 0.0;
118 		avgFPS = 0.0;
119 	}
120 	/**
121 	 * Returns a copy of the palette of the object.
122 	 */
123 	public @property Color[] palette() @safe pure nothrow @nogc {
124 		return _palette;
125 	}
126 	public Color getPaletteIndex(ushort index) @safe pure nothrow @nogc const {
127 		return _palette[index];
128 	}
129 	public Color setPaletteIndex(ushort index, Color val) @safe pure nothrow @nogc {
130 		return _palette[index] = val;
131 	}
132 	/**
133 	 * Loads a palette into the object.
134 	 * Returns the new palette of the object.
135 	 */
136 	public Color[] loadPalette(Color[] palette) @safe {
137 		return _palette = palette;
138 	}
139 	/**
140 	 * Adds a palette chunk to the end of the main palette.
141 	 */
142 	public Color[] addPaletteChunk(Color[] paletteChunk) @safe {
143 		return _palette ~= paletteChunk;
144 	}
145 	/**
146 	 * Loads a palette chunk into the object.
147 	 * The offset determines where the palette should be loaded.
148 	 * If it points to an existing place, the indices after that will be overwritten until the whole palette will be copied.
149 	 * If it points to the end or after it, then the palette will be made longer, and will pad with values 0x00_00_00_00 if needed.
150 	 * Returns the new palette of the object.
151 	 */
152 	public Color[] loadPaletteChunk(Color[] paletteChunk, ushort offset) @safe {
153 		if (paletteChunk.length + offset > _palette.length) 
154 			_palette.length = offset + paletteChunk.length;
155 		for (int i = offset, j ; j < paletteChunk.length ; i++, j++) 
156 			_palette[i] = paletteChunk[j];
157 		return _palette;
158 	}
159 	/**
160 	 * Clears an area of the palette with zeroes.
161 	 * Returns the original area.
162 	 */
163 	public Color[] clearPaletteChunk(ushort lenght, ushort offset) @safe {
164 		Color[] backup = _palette[offset..offset + lenght].dup;
165 		for (int i = offset ; i < offset + lenght ; i++) {
166 			_palette[i] = Color(0);
167 		}
168 		return backup;
169 	}
170 	/**
171 	 * Returns the current FPS count.
172 	 */
173 	public @property real fps() @safe @nogc pure nothrow const {
174 		return framesPerSecond;
175 	}
176 	/**
177 	 * Returns the current average FPS count.
178 	 */
179 	public @property real avgfps() @safe @nogc pure nothrow const {
180 		return avgFPS;
181 	}
182 	/**
183 	 * Resets the avgFPS to zero.
184 	 */
185 	public void resetAvgfps() @safe @nogc pure nothrow {
186 		avgFPS = 0;
187 	}
188 	~this(){
189 		foreach(SDL_Texture* t ; frameBuffer){
190 			if(t)
191 				SDL_DestroyTexture(t);
192 		}
193 	}
194     ///Adds a RefreshListener to its list.
195     public void addRefreshListener(RefreshListener r){
196         rL ~= r;
197     }
198 	///Edits the given color index.
199 	///Will be set deprecated in 0.10.0
200 	public void editColor(ushort c, Color val){
201 		_palette[c] = val;
202 	}
203 	///Sets the number of colors.
204 	///Will be set deprecated in 0.10.0
205 	public void setupPalette(int i) {
206 		_palette.length = i;
207 	}
208     ///Replaces the layer at the given number.
209 	///Deprecated!
210     public deprecated void replaceLayer(Layer l, int i){
211 		addLayer(l, i);
212     }
213     ///Adds a layer at the given priority.
214     public void addLayer(Layer l, int i) @safe pure nothrow {
215 		l.setRasterizer(rX, rY);
216 		layerMap[i] = l;
217     }
218 	public void loadLayers(R)(R layerRange) @safe pure nothrow {
219 		foreach (int key, Layer value; layerRange) {
220 			addLayer(value, key);
221 		}
222 	}
223 	///Removes a layer at the given priority.
224 	public void removeLayer(int n) @safe pure nothrow {
225 		layerMap.remove(n);
226 	}
227 	public Layer getLayer(int n) @nogc @safe pure nothrow {
228 		return layerMap[n];
229 	}
230 	/**
231 	 * Refreshes the whole framebuffer.
232 	 */
233     public void refresh() {
234 		import std.stdio : writeln;
235         r = true;
236 		
237 		//get frame duration
238 		frameTime_1 = frameTime;
239 		frameTime = MonoTimeImpl!(ClockType.normal).currTime();
240 		delta_frameTime = frameTime - frameTime_1;
241 		const real delta_frameTime0 = cast(real)(delta_frameTime.total!"usecs"());
242 		framesPerSecond = 1 / (delta_frameTime0 / 1_000_000);
243 		if(avgFPS)
244 			avgFPS = (avgFPS + framesPerSecond) / 2;
245 		else
246 			avgFPS = framesPerSecond;
247 
248 		updatedBuffer++;
249 		if(updatedBuffer >= nOfBuffers) updatedBuffer = 0;
250 
251 		SDL_LockTexture(frameBuffer[updatedBuffer], null, &fbData, &fbPitch);
252 
253 		/+for(int i ; i < layerPriorityHandler.length ; i++){
254 			layerList[i].updateRaster(fbData, fbPitch, palette.ptr);
255 		}+/
256 		foreach (Layer layer ; layerMap) {
257 			layer.updateRaster(fbData, fbPitch, palette.ptr);
258 		}
259 
260 		SDL_UnlockTexture(frameBuffer[updatedBuffer]);
261         r = false;
262 
263         foreach(r; rL){
264             r.refreshFinished;
265         }
266 		
267     }
268 
269 
270     ///Returns the workpad.
271     public SDL_Texture* getOutput() @nogc @safe pure nothrow {
272 		if (displayedBuffer == updatedBuffer) displayedBuffer++;
273 		if (displayedBuffer >= nOfBuffers) displayedBuffer = 0;
274 		return frameBuffer[displayedBuffer++];
275 		//return frameBuffer[0];
276     }
277 
278 
279     ///Returns if the raster is refreshing.
280     public bool isRefreshing(){
281         return r;
282     }
283 }