1 /*
2 * Copyright (C) 2015-2017, by Laszlo Szeremi under the Boost license.
3 *
4 * Pixel Perfect Engine, graphics.bitmap module
5 */6 modulepixelperfectengine.graphics.raster;
7 8 importpixelperfectengine.graphics.outputscreen;
9 importpixelperfectengine.graphics.layers;
10 importpixelperfectengine.graphics.bitmap;
11 importbindbc.sdl;
12 publicimportpixelperfectengine.graphics.common;
13 importstd.conv;
14 importstd.algorithm.sorting;
15 importstd.algorithm.mutation;
16 importcore.time;
17 importcollections.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 publicinterfaceRefreshListener{
22 publicvoidrefreshFinished();
23 }
24 25 ///Used to read the output from the raster and show it on the screen.26 publicinterfaceIRaster{
27 publicSDL_Texture* getOutput();
28 }
29 /**
30 * Defines palette handling functions.
31 * It provides various functions for safe palette handling.
32 */33 publicinterfacePaletteContainer {
34 /**
35 * Returns the palette of the object.
36 */37 public @propertyColor[] palette() @safepurenothrow @nogc;
38 ///Returns the given palette index.39 publicColorgetPaletteIndex(ushortindex) @safepurenothrow @nogcconst;
40 ///Sets the given palette index to the given value.41 publicColorsetPaletteIndex(ushortindex, Colorvalue) @safepurenothrow @nogc;
42 /**
43 * Adds a palette chunk to the end of the main palette.
44 */45 publicColor[] addPaletteChunk(Color[] paletteChunk) @safe;
46 /**
47 * Loads a palette into the object.
48 * Returns the new palette of the object.
49 */50 publicColor[] 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 publicColor[] loadPaletteChunk(Color[] paletteChunk, ushortoffset) @safe;
59 /**
60 * Clears an area of the palette with zeroes.
61 * Returns the original area.
62 */63 publicColor[] clearPaletteChunk(ushortlenght, ushortoffset) @safe;
64 }
65 66 ///Handles multiple layers onto one framebuffer.67 publicclassRaster : IRaster, PaletteContainer{
68 privateushortrX, rY; ///Stores screen resolution. Set overscan resolutions at OutputWindow69 //public SDL_Surface* workpad;70 publicSDL_Texture*[] frameBuffer;
71 publicvoid* fbData;
72 publicintfbPitch;
73 /**
74 * Color format is ARGB, with each index having their own transparency.
75 */76 protectedColor[] _palette;
77 aliasLayerMap = TreeMap!(int, Layer);
78 ///Stores the layers by their priorities.79 publicLayerMaplayerMap;
80 //private Layer[int] layerList; 81 privateint[] threads; ///Thread IDs (currently unused).82 privateboolr; ///Set to true if refresh is happening.83 protectedubytenOfBuffers; ///Number of framebuffers, 2 for double buffering.84 protectedubyteupdatedBuffer; ///Framebuffer currently being updated85 protectedubytedisplayedBuffer;///Framebuffer currently being displayed86 //private int[2] doubleBufferRegisters;87 privateRefreshListener[] rL; ///Contains RefreshListeners associated with this raster.88 privateMonoTimeframeTime, frameTime_1; ///Timestamps of frame occurences89 privateDurationdelta_frameTime; ///Current time delta between two frames90 privaterealframesPerSecond, avgFPS; ///Current and average fps counter91 //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 can99 ///eliminate occassional flickering and latency at the possibility of screen tearing and having to wait for100 ///the buffer to be finished.101 publicthis(ushortx, ushorty, OutputScreenoW, size_tpaletteLength, ubytebuffers = 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 (inti ; 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 @propertyColor[] palette() @safepurenothrow @nogc {
124 return_palette;
125 }
126 publicColorgetPaletteIndex(ushortindex) @safepurenothrow @nogcconst {
127 return_palette[index];
128 }
129 publicColorsetPaletteIndex(ushortindex, Colorval) @safepurenothrow @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 publicColor[] loadPalette(Color[] palette) @safe {
137 return_palette = palette;
138 }
139 /**
140 * Adds a palette chunk to the end of the main palette.
141 */142 publicColor[] 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 publicColor[] loadPaletteChunk(Color[] paletteChunk, ushortoffset) @safe {
153 if (paletteChunk.length + offset > _palette.length)
154 _palette.length = offset + paletteChunk.length;
155 for (inti = 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 publicColor[] clearPaletteChunk(ushortlenght, ushortoffset) @safe {
164 Color[] backup = _palette[offset..offset + lenght].dup;
165 for (inti = offset ; i < offset + lenght ; i++) {
166 _palette[i] = Color(0);
167 }
168 returnbackup;
169 }
170 /**
171 * Returns the current FPS count.
172 */173 public @propertyrealfps() @safe @nogcpurenothrowconst {
174 returnframesPerSecond;
175 }
176 /**
177 * Returns the current average FPS count.
178 */179 public @propertyrealavgfps() @safe @nogcpurenothrowconst {
180 returnavgFPS;
181 }
182 /**
183 * Resets the avgFPS to zero.
184 */185 publicvoidresetAvgfps() @safe @nogcpurenothrow {
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 publicvoidaddRefreshListener(RefreshListenerr){
196 rL ~= r;
197 }
198 ///Sets the number of colors.199 publicvoidsetupPalette(inti) {
200 _palette.length = i;
201 }
202 ///Adds a layer at the given priority.203 publicvoidaddLayer(Layerl, inti) @safepurenothrow {
204 l.setRasterizer(rX, rY);
205 layerMap[i] = l;
206 }
207 publicvoidloadLayers(R)(RlayerRange) @safepurenothrow {
208 foreach (intkey, Layervalue; layerRange) {
209 addLayer(value, key);
210 }
211 }
212 ///Removes a layer at the given priority.213 publicvoidremoveLayer(intn) @safepurenothrow {
214 layerMap.remove(n);
215 }
216 publicLayergetLayer(intn) @nogc @safepurenothrow {
217 returnlayerMap[n];
218 }
219 /**
220 * Refreshes the whole framebuffer.
221 */222 publicvoidrefresh() {
223 importstd.stdio : writeln;
224 r = true;
225 226 //get frame duration227 frameTime_1 = frameTime;
228 frameTime = MonoTimeImpl!(ClockType.normal).currTime();
229 delta_frameTime = frameTime - frameTime_1;
230 constrealdelta_frameTime0 = cast(real)(delta_frameTime.total!"usecs"());
231 framesPerSecond = 1 / (delta_frameTime0 / 1_000_000);
232 if(avgFPS)
233 avgFPS = (avgFPS + framesPerSecond) / 2;
234 else235 avgFPS = framesPerSecond;
236 237 updatedBuffer++;
238 if(updatedBuffer >= nOfBuffers) updatedBuffer = 0;
239 240 SDL_LockTexture(frameBuffer[updatedBuffer], null, &fbData, &fbPitch);
241 242 /+for(int i ; i < layerPriorityHandler.length ; i++){
243 layerList[i].updateRaster(fbData, fbPitch, palette.ptr);
244 }+/245 foreach (Layerlayer ; layerMap) {
246 layer.updateRaster(fbData, fbPitch, palette.ptr);
247 }
248 249 SDL_UnlockTexture(frameBuffer[updatedBuffer]);
250 r = false;
251 252 foreach(r; rL){
253 r.refreshFinished;
254 }
255 256 }
257 258 259 ///Returns the workpad.260 publicSDL_Texture* getOutput() @nogc @safepurenothrow {
261 if (displayedBuffer == updatedBuffer) displayedBuffer++;
262 if (displayedBuffer >= nOfBuffers) displayedBuffer = 0;
263 returnframeBuffer[displayedBuffer++];
264 //return frameBuffer[0];265 }
266 267 268 ///Returns if the raster is refreshing.269 publicboolisRefreshing(){
270 returnr;
271 }
272 }