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