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 }