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 }