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 derelict.sdl2.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 
18 ///The raster calls it every time it finishes the drawing to the framebuffers.
19 public interface RefreshListener{
20     public void refreshFinished();
21 }
22 
23 ///Used to read the output from the raster and show it on the screen.
24 public interface IRaster{
25     public SDL_Texture* getOutput();
26 }
27 
28 ///Handles multiple layers onto one framebuffer.
29 public class Raster : IRaster{
30     private ushort rX, rY;		///Stores screen resolution
31     //public SDL_Surface* workpad;
32 	public SDL_Texture*[] frameBuffer;
33 	public void*[] fbData;
34 	public int[] fbPitch;
35     public Color[] palette; ///FORMAT IS ARGB. Master palette, layers or bitmaps can define their own palettes if needed.
36     private Layer[int] layerList;	///Stores the layers by their priorities.
37 	private int[] layerPriorityHandler, threads;
38     private bool r;
39 	private int[2] doubleBufferRegisters;
40     private RefreshListener[] rL;
41 	private MonoTime frameTime, frameTime_1;
42 	private Duration delta_frameTime;
43 	private real framesPerSecond, avgFPS;
44 	//public Bitmap16Bit[2] frameBuffer;
45 
46     ///Default constructor. x and y : represent the resolution of the raster.
47     public this(ushort x, ushort y, OutputScreen oW, int[] threads = [0]){
48         //workpad = SDL_CreateRGBSurface(SDL_SWSURFACE, x, y, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
49 		this.threads = threads;
50 		SDL_Renderer* renderer = oW.renderer;
51         rX=x;
52         rY=y;
53 		/*frameBuffer[0] = new Bitmap16Bit(x,y);
54 		frameBuffer[1] = new Bitmap16Bit(x,y);*/
55 		/*frameBuffer ~= SDL_CreateRGBSurface(SDL_SWSURFACE, x, y, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
56 		frameBuffer ~= SDL_CreateRGBSurface(SDL_SWSURFACE, x, y, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);*/
57 		frameBuffer ~= SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGRX8888, SDL_TEXTUREACCESS_STREAMING, x, y);
58 		frameBuffer ~= SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGRX8888, SDL_TEXTUREACCESS_STREAMING, x, y);
59 		fbData ~= null;
60 		fbData ~= null;
61 		fbPitch ~= 0;
62 		fbPitch ~= 0;
63 		doubleBufferRegisters[0] = 1;
64 		doubleBufferRegisters[1] = 0;
65 		oW.setMainRaster(this);
66 		addRefreshListener(oW);
67 	}
68 	public @nogc @property real fps(){
69 		return framesPerSecond;
70 	}
71 	public @nogc @property real avgfps(){
72 		return avgFPS;
73 	}
74 	public @nogc void resetAvgfps(){
75 		avgFPS = 0;
76 	}
77 	~this(){
78 		foreach(SDL_Texture* t ; frameBuffer){
79 			if(t)
80 				SDL_DestroyTexture(t);
81 		}
82 	}
83     //Adds a RefreshListener to its list.
84     public void addRefreshListener(RefreshListener r){
85         rL ~= r;
86     }
87 	///Writes a color at the last position
88 	public void addColor(Color val){
89 		palette ~= val;
90 	}
91 
92 	public void editColor(ushort c, Color val){
93 		palette[c] = val;
94 	}
95 	//Sets the number of colors.
96 	public void setupPalette(int i){
97 		palette.length = i;
98 	}
99     ///Replaces the layer at the given number.
100     public void replaceLayer(Layer l, int i){
101 		l.setRasterizer(rX, rY);
102         layerList[i] = l;
103     }
104     ///Adds a layer at the given priority.
105     public void addLayer(Layer l, int i){
106 		l.setRasterizer(rX, rY);
107         layerList[i] = l;
108 		layerPriorityHandler ~= i;
109 		layerPriorityHandler.sort();
110     }
111 	public void removeLayer(int n){
112 		layerList.remove(n);
113 		int[] newlayerPriorityHandler;
114 		for(int i; i < layerPriorityHandler.length; i++){
115 			//writeln(0);
116 			if(layerPriorityHandler[i] != n){
117 				newlayerPriorityHandler ~= layerPriorityHandler[i];
118 
119 			}
120 		}
121 		layerPriorityHandler = newlayerPriorityHandler;
122 	}
123 	/**
124 	 * Refreshes the whole framebuffer. 
125 	 */
126     public void refresh(){
127 
128         r = true;
129 		//this.clearFramebuffer();
130 		if(doubleBufferRegisters[0] == 0){
131 			doubleBufferRegisters[0] = 1;
132 			doubleBufferRegisters[1] = 0;
133 		}else{
134 			doubleBufferRegisters[0] = 0;
135 			doubleBufferRegisters[1] = 1;
136 		}
137 
138 		SDL_LockTexture(frameBuffer[doubleBufferRegisters[0]], null, &fbData[doubleBufferRegisters[0]], &fbPitch[doubleBufferRegisters[0]]);
139 
140 		for(int i ; i < layerPriorityHandler.length ; i++){
141 			layerList[layerPriorityHandler[i]].updateRaster(fbData[doubleBufferRegisters[0]], fbPitch[doubleBufferRegisters[0]], palette.ptr, threads);
142 		}
143         
144 		SDL_UnlockTexture(frameBuffer[doubleBufferRegisters[0]]);
145         r = false;
146 
147         foreach(r; rL){
148             r.refreshFinished;
149         }
150 		//get frame duration
151 		frameTime_1 = frameTime;
152 		frameTime = MonoTimeImpl!(ClockType.normal).currTime();
153 		delta_frameTime = frameTime_1 - frameTime;
154 		real delta_frameTime0 = to!real(delta_frameTime.total!"hnsecs"());
155 		framesPerSecond = framesPerSecond + 1 / (delta_frameTime0 / 10000);
156 		if(avgFPS)
157 			avgFPS = (avgFPS + framesPerSecond) / 2;
158 		else
159 			avgFPS = framesPerSecond;
160     }
161 
162 	
163     ///Returns the workpad.
164     public SDL_Texture* getOutput(){
165 		if(fbData[0] !is null)
166 			return frameBuffer[0];
167 		return frameBuffer[1];
168     }
169     
170     
171     ///Returns if the raster is refreshing.
172     public bool isRefreshing(){
173         return r;
174     }
175 }