1 module windows.rasterwindow;
2 
3 /*
4  * rasterWindow.d
5  *
6  * Outputs layers to a window with the capability of temporarily removing them
7  */
8 import pixelperfectengine.concrete.window;
9 import pixelperfectengine.graphics.layers;
10 import pixelperfectengine.graphics.raster : PaletteContainer;
11 import CPUblit.composing;
12 static import CPUblit.draw;
13 import CPUblit.colorlookup;
14 import pixelperfectengine.system.input.types : MouseButton, ButtonState;
15 import collections.sortedlist;
16 
17 import document;
18 debug import std.stdio;
19 
20 /**
21  * Implements a subraster using a window. Has the capability of skipping over individual layers.
22  */
23 public class RasterWindow : Window, PaletteContainer {
24 	alias DisplayList = SortedList!(int, "a < b", false);
25 	public DisplayList	hiddenLayers;	///List of hidden layers
26 	public DisplayList	soloedLayers;	///List of soloed layers
27 	protected Bitmap32Bit trueOutput, rasterOutput;
28 	protected Color[] paletteLocal;
29 	protected Color* paletteShared;
30 	public Color		selColor;		///Selection invert color (red by default)
31 	public Color		selArmColor;	///Selection armed color (blue by default)
32 	protected uint statusFlags;
33 	protected static enum MOVE_ARMED = 1 << 0; 		///Redirect mouse events to document
34 	protected static enum CLOSE_PROTECT = 1 << 1;
35 	protected static enum SELECTION_ARMED = 1 << 2;	///Selection is armed, draw box, and redirect event to document
36 	protected static enum SHOW_SELECTION = 1 << 3;	///Shows selection
37 	//protected int[] layerList;
38 	public int rasterX, rasterY;		///Raster sizes
39 	protected dstring documentName;
40 	protected MapDocument document;
41 	///The selection area on the screen. Converted from the parent document's own absolute values
42 	public Box selection;
43 	protected RadioButtonGroup modeSel;
44 	/**
45 	 * Creates a new RasterWindow.
46 	 */
47 	public this(int x, int y, Color* paletteShared, dstring documentName, MapDocument document){
48 		rasterX = x;
49 		rasterY = y;
50 		trueOutput = new Bitmap32Bit(x + 2,y + 18);
51 		rasterOutput = new Bitmap32Bit(x + 2, y + 18);
52 		ISmallButton[] smallButtons;
53 		const int windowHeaderHeight = getStyleSheet.drawParameters["WindowHeaderHeight"] - 1;
54 		modeSel = new RadioButtonGroup();
55 		smallButtons ~= closeButton();
56 		smallButtons ~= new SmallButton("settingsButtonB", "settingsButtonA", "settings", Box(0, 0, windowHeaderHeight, 
57 				windowHeaderHeight));
58 		smallButtons ~= new SmallButton("paletteButtonB", "paletteButtonA", "palette", Box(0, 0, windowHeaderHeight, 
59 				windowHeaderHeight));
60 		smallButtons ~= new RadioButton("selMoveButtonB", "selMoveButtonA", "selMove", 
61 				Box(0, 0, windowHeaderHeight, windowHeaderHeight), modeSel);
62 		smallButtons ~= new RadioButton("tilePlacementButtonB", "tilePlacementButtonA", "tile", 
63 				Box(0, 0, windowHeaderHeight, windowHeaderHeight), modeSel);
64 		smallButtons ~= new RadioButton("objPlacementButtonB", "objPlacementButtonA", "obj", 
65 				Box(0, 0, windowHeaderHeight, windowHeaderHeight), modeSel);
66 		smallButtons ~= new RadioButton("sprtPlacementButtonB", "sprtPlacementButtonA", "sprt", 
67 				Box(0, 0, windowHeaderHeight, windowHeaderHeight), modeSel);
68 		modeSel.onToggle = &onModeToggle;
69 
70 		//smallButtons ~= new SmallButton("settingsButtonB", "settingsButtonA", "settings", Box(0, 0, 16, 16));
71 		super(Box(0, 0, x + 1, y + 17), documentName, smallButtons);
72 		foreach (ISmallButton key; smallButtons) {
73 			WindowElement we = cast(WindowElement)key;
74 			we.onDraw = &clrLookup;
75 		}
76 		this.paletteShared = paletteShared;
77 		this.documentName = documentName;
78 		this.document = document;
79 		statusFlags |= CLOSE_PROTECT;
80 		modeSel.latchPos(0);
81 		selColor = Color(0xff,0x00,0x00,0x00);
82 		selArmColor = Color(0x00,0x00,0xff,0x00);
83 	}
84 	/**
85 	 * Overrides the original getOutput function to return a 32 bit bitmap instead.
86 	 */
87 	public override @property ABitmap getOutput(){
88 		return trueOutput;
89 	}
90 	/**
91 	 * Returns the palette of the object.
92 	 */
93 	public @property Color[] palette() @safe pure nothrow @nogc {
94 		return paletteLocal;
95 	}
96 	///Returns the given palette index.
97 	public Color getPaletteIndex(ushort index) @safe pure nothrow @nogc const {
98 		return paletteLocal[index];
99 	}
100 	///Sets the given palette index to the given value.
101 	public Color setPaletteIndex(ushort index, Color value) @safe pure nothrow @nogc {
102 		return paletteLocal[index] = value;
103 	}
104 	/**
105 	 * Adds a palette chunk to the end of the main palette.
106 	 */
107 	public Color[] addPaletteChunk(Color[] paletteChunk) @safe {
108 		return paletteLocal ~= paletteChunk;
109 	}
110 	/**
111 	 * Loads a palette into the object.
112 	 * Returns the new palette of the object.
113 	 */
114 	public Color[] loadPalette(Color[] palette) @safe {
115 		return paletteLocal = palette;
116 	}
117 	/**
118 	 * Loads a palette chunk into the object.
119 	 * The offset determines where the palette should be loaded.
120 	 * If it points to an existing place, the indices after that will be overwritten until the whole palette will be copied.
121 	 * If it points to the end or after it, then the palette will be made longer, and will pad with values #00000000 if needed.
122 	 * Returns the new palette of the object.
123 	 */
124 	public Color[] loadPaletteChunk(Color[] paletteChunk, ushort offset) @safe {
125 		if (paletteLocal.length < offset + paletteChunk.length) {
126 			paletteLocal.length = paletteLocal.length + (offset - paletteLocal.length) + paletteChunk.length;
127 		}
128 		assert(paletteLocal.length >= offset + paletteChunk.length, "Palette error!");
129 		for (int i ; i < paletteChunk.length ; i++) {
130 			paletteLocal[i + offset] = paletteChunk[i];
131 		}
132 		return paletteLocal;
133 	}
134 	/**
135 	 * Clears an area of the palette with zeroes.
136 	 * Returns the original area.
137 	 */
138 	public Color[] clearPaletteChunk(ushort lenght, ushort offset) @safe {
139 		Color[] backup = paletteLocal[offset..offset + lenght].dup;
140 		for (int i = offset ; i < offset + lenght ; i++) {
141 			paletteLocal[i] = Color(0);
142 		}
143 		return backup;
144 	}
145 	//public override void passMouseEvent(int x, int y, int state, ubyte button) {
146 	///Passes mouse click event
147 	public override void passMCE(MouseEventCommons mec, MouseClickEvent mce) {
148 		StyleSheet ss = getStyleSheet;
149 		if (mce.y >= position.top + ss.drawParameters["WindowHeaderHeight"] && mce.y < position.bottom &&
150 				mce.x > position.left && mce.x < position.right) {
151 			mce.y -= ss.drawParameters["WindowHeaderHeight"] + position.top;
152 			mce.x -= position.left - 1;
153 			document.passMCE(mec, mce);
154 		} else {
155 			super.passMCE(mec, mce);
156 		}
157 	}
158 	///Passes mouse move event
159 	public override void passMME(MouseEventCommons mec, MouseMotionEvent mme) {
160 		StyleSheet ss = getStyleSheet;
161 		if (statusFlags & (MOVE_ARMED | SELECTION_ARMED) && /+mme.buttonState == (1<<MouseButton.Mid) &&+/ 
162 				mme.y >= position.top + ss.drawParameters["WindowHeaderHeight"] && mme.y < position.bottom &&
163 				mme.x > position.left && mme.x < position.right) {
164 			mme.y -= ss.drawParameters["WindowHeaderHeight"] + position.top;
165 			mme.x -= position.left - 1;
166 			document.passMME(mec, mme);
167 			
168 		} else {
169 			super.passMME(mec, mme);
170 		}
171 	}
172 	/**
173 	 * Copy 8 bit bitmap with color lookup.
174 	 */
175 	protected void clrLookup() {
176 		for(int y ; y < 16 ; y++){	//
177 			colorLookup(output.output.getPtr + (y * position.width), trueOutput.getPtr + (y * position.width), paletteShared,
178 					position.width);
179 		}
180 	}
181 	public override void draw(bool drawHeaderOnly = false){
182 		if(output.output.width != position.width || output.output.height != position.height){
183 			output = new BitmapDrawer(position.width(), position.height());
184 			trueOutput = new Bitmap32Bit(position.width(), position.height());
185 			rasterOutput = new Bitmap32Bit(position.width() - 2, position.height() - 18);
186 		}
187 
188 		drawHeader();
189 		clrLookup();
190 		updateRaster();
191 		if (statusFlags & SELECTION_ARMED) {
192 			
193 		}
194 		/*if(drawHeaderOnly)
195 			return;*/
196 		//draw the borders. we do not need fills or drawing elements
197 		uint* ptr = cast(uint*)trueOutput.getPtr;
198 		StyleSheet ss = getStyleSheet;
199 		CPUblit.draw.drawLine!uint(0, 16, 0, position.height - 1, paletteShared[ss.getColor("windowascent")].base, ptr, 
200 				trueOutput.width);
201 		CPUblit.draw.drawLine!uint(0, 16, position.width - 1, 16, paletteShared[ss.getColor("windowascent")].base, ptr, 
202 				trueOutput.width);
203 		CPUblit.draw.drawLine!uint(position.width - 1, 16, position.width - 1, position.height - 1,
204 				paletteShared[ss.getColor("windowdescent")].base, ptr, trueOutput.width);
205 		CPUblit.draw.drawLine!uint(0, position.height - 1, position.width - 1, position.height - 1,
206 				paletteShared[ss.getColor("windowdescent")].base, ptr, trueOutput.width);
207 	}
208 	/**
209 	 * Clears both displaylists.
210 	 */
211 	public void clearDisplayLists() {
212 		hiddenLayers = DisplayList([]);
213 		soloedLayers = DisplayList([]);
214 	}
215 	/**
216 	 * Updates the raster of the window.
217 	 */
218 	public void updateRaster() {
219 		//clear raster screen
220 		for (int y = 16 ; y < trueOutput.height - 1 ; y++) {
221 			for (int x = 1 ; x < trueOutput.width - 1 ; x++) {
222 				trueOutput.writePixel (x, y, Color(0,0,0,0));
223 			}
224 		}
225 		//update each layer individually
226 		foreach (int i, Layer l ; document.mainDoc.layeroutput) {
227 			if ((i !in hiddenLayers && !soloedLayers.length) || (i in soloedLayers && soloedLayers.length))
228 				l.updateRaster((trueOutput.getPtr + (17 * trueOutput.width) + 1), trueOutput.width * 4, paletteLocal.ptr);
229 		}
230 		/+for(int i ; i < layerList.length ; i++){
231 			//document.mainDoc[layerList[i]].updateRaster(rasterOutput.getPtr, rasterX * 4, paletteLocal.ptr);
232 			document.mainDoc[layerList[i]].updateRaster((trueOutput.getPtr + (17 * trueOutput.width) + 1), trueOutput.width * 4,
233 					paletteLocal.ptr);
234 		}+/
235 		for (int i = 16 ; i < trueOutput.height - 1 ; i++) {
236 			helperFunc(trueOutput.getPtr + 1 + trueOutput.width * i, trueOutput.width - 2);
237 		}
238 		import CPUblit.composing.specblt : xorBlitter;
239 		uint* p = cast(uint*)trueOutput.getPtr;
240 		if (statusFlags & SELECTION_ARMED) {
241 			for (int y = selection.top + 16 ; y <= selection.bottom + 16 ; y++) {
242 				xorBlitter!uint(p + 1 + trueOutput.width * y + selection.left, selection.width, selArmColor.base);
243 			}
244 		} else if (statusFlags & SHOW_SELECTION) {
245 			for (int y = selection.top + 16 ; y <= selection.bottom + 16 ; y++) {
246 				xorBlitter!uint(p + 1 + trueOutput.width * y + selection.left, selection.width, selColor.base);
247 			}
248 		}
249 	}
250 	/+/**
251 	 * Adds a new layer then reorders the display list.
252 	 */
253 	public void addLayer(int p) {
254 		import std.algorithm.sorting : sort;
255 		layerList ~= p;
256 		layerList.sort();
257 	}
258 	/**
259 	 * Removes a layer then reorders the display list.
260 	 */
261 	public void removeLayer(int p) {
262 		import std.algorithm.mutation : remove;
263 		for (int i ; i < layerList.length ; i++) {
264 			if (layerList[i] == p) {
265 				layerList.remove(i);
266 				return;
267 			}
268 		}
269 	}+/
270 	/**
271 	 * Copies and sets all alpha values to 255 to avoid transparency issues
272 	 */
273 	protected @nogc void helperFunc(void* src, size_t length) pure nothrow {
274 		import pixelperfectengine.system.platform;
275 		static if(USE_INTEL_INTRINSICS){
276 			import inteli.emmintrin;
277 			immutable ubyte[16] ALPHA_255_VEC = [255,0,0,0,255,0,0,0,255,0,0,0,255,0,0,0];
278 			while(length > 4){
279 				_mm_storeu_si128(cast(__m128i*)src, _mm_loadu_si128(cast(__m128i*)src) |
280 						_mm_loadu_si128(cast(__m128i*)(cast(void*)ALPHA_255_VEC.ptr)));
281 				src += 16;
282 				//dest += 16;
283 				length -= 4;
284 			}
285 			while(length){
286 				*cast(uint*)src = *cast(uint*)src | 0x00_00_00_FF;
287 				src += 4;
288 				//dest += 4;
289 				length--;
290 			}
291 		}else{
292 			while(length){
293 				*cast(uint*)src = *cast(uint*)src | 0x00_00_00_FF;
294 				src += 4;
295 				dest += 4;
296 				length--;
297 			}
298 		}
299 	}
300 	/**
301 	 * Overrides the original onExit function for safe close.
302 	 */
303 	public override void close() {
304 		if (statusFlags & CLOSE_PROTECT) {
305 
306 		} else {
307 			super.close;
308 		}
309 	}
310 	
311 	///Called when selection needs to be armed.
312 	public void armSelection() @nogc @safe pure nothrow {
313 		statusFlags |= SELECTION_ARMED;
314 	}
315 	///Called when selection needs to be disarmed.
316 	public void disarmSelection() @nogc @safe pure nothrow {
317 		statusFlags &= ~SELECTION_ARMED;
318 	}
319 	///Returns true if selection is armed
320 	public bool isSelectionArmed() const @nogc @safe pure nothrow {
321 		return statusFlags & SELECTION_ARMED ? true : false;
322 	}
323 	///Called when selection needs to be disarmed.
324 	public bool showSelection(bool val) @nogc @safe pure nothrow {
325 		if (val) statusFlags |= SHOW_SELECTION;
326 		else statusFlags &= ~SHOW_SELECTION;
327 		return statusFlags & SHOW_SELECTION ? true : false;
328 	}
329 	///Returns true if selection is displayed
330 	public bool showSelection() const @nogc @safe pure nothrow {
331 		return statusFlags & SHOW_SELECTION ? true : false;
332 	}
333 	///Enables or disables move
334 	public bool moveEn(bool val) @nogc @safe pure nothrow {
335 		if (val) statusFlags |= MOVE_ARMED;
336 		else statusFlags &= ~MOVE_ARMED;
337 		return statusFlags & MOVE_ARMED;
338 	}
339 	///Called when the document settings window is needed to be opened.
340 	public void onDocSettings(Event ev) {
341 
342 	}
343 	///Called when any of the modes are selected.
344 	public void onModeToggle(Event ev) {
345 		switch (modeSel.value) {
346 			case "tile":
347 				document.mode = MapDocument.EditMode.tilePlacement;
348 				break;
349 			case "obj":
350 				document.mode = MapDocument.EditMode.boxPlacement;
351 				break;
352 			case "sprt":
353 				document.mode = MapDocument.EditMode.spritePlacement;
354 				break;
355 			default:
356 				document.mode = MapDocument.EditMode.selectDragScroll;
357 				break;
358 		}
359 		document.clearSelection();
360 	}
361 
362 	public void loadLayers () {
363 		foreach (key; document.mainDoc.layeroutput) {
364 			key.setRasterizer(rasterX, rasterY);
365 			//addLayer(key);
366 		}
367 	}
368 }