From 64b822b903247faa72802342ff63e35690b36f14 Mon Sep 17 00:00:00 2001
From: MaxED <j.maxed@gmail.com>
Date: Wed, 30 Dec 2015 11:53:03 +0000
Subject: [PATCH] Implemented, TEXTURES: ZDoom can use any graphic as a texture
 patch. Fixed a typo.

---
 Source/Core/Config/ResourceTextureSet.cs |   2 +-
 Source/Core/Data/HighResImage.cs         | 376 +++++++++++++----------
 Source/Core/Editing/ThingsFilter.cs      |   2 +-
 3 files changed, 208 insertions(+), 172 deletions(-)

diff --git a/Source/Core/Config/ResourceTextureSet.cs b/Source/Core/Config/ResourceTextureSet.cs
index ca46c5f69..edaf0423c 100644
--- a/Source/Core/Config/ResourceTextureSet.cs
+++ b/Source/Core/Config/ResourceTextureSet.cs
@@ -73,7 +73,7 @@ namespace CodeImp.DoomBuilder.Config
 		// Add a flat
 		internal void AddFlat(ImageData image)
 		{
-			if(flats.ContainsKey(image.LongName))
+			if(flats.ContainsKey(image.LongName) && (!General.Map.Config.MixTexturesFlats || !image.HasPatchWithSameName))
 				General.ErrorLogger.Add(ErrorType.Warning, "Flat \"" + image.Name + "\" is double defined in resource \"" + this.Location.location + "\".");
 			flats[image.LongName] = image;
 		}
diff --git a/Source/Core/Data/HighResImage.cs b/Source/Core/Data/HighResImage.cs
index 03a6b8524..c0b3bdbd1 100644
--- a/Source/Core/Data/HighResImage.cs
+++ b/Source/Core/Data/HighResImage.cs
@@ -129,7 +129,7 @@ namespace CodeImp.DoomBuilder.Data
 					loadfailed = true;
 				}
 
-				int failCount = 0; //mxd
+				int missingpatches = 0; //mxd
 
 				if(!loadfailed)
 				{
@@ -157,11 +157,12 @@ namespace CodeImp.DoomBuilder.Data
 								{
 									reader = ImageDataFormat.GetImageReader(mem, ImageDataFormat.DOOMFLAT, General.Map.Data.Palette);
 								}
+
 								if(reader is UnknownImageReader) 
 								{
 									// Data is in an unknown format!
 									General.ErrorLogger.Add(ErrorType.Error, "Patch lump '" + p.lumpname + "' data format could not be read, while loading texture '" + this.Name + "'");
-									failCount++; //mxd
+									missingpatches++; //mxd
 								}
 							}
 
@@ -175,176 +176,14 @@ namespace CodeImp.DoomBuilder.Data
 								{
 									// Data cannot be read!
 									General.ErrorLogger.Add(ErrorType.Error, "Patch lump '" + p.lumpname + "' data format could not be read, while loading texture '" + this.Name + "'");
-									failCount++; //mxd
+									missingpatches++; //mxd
 								}
+
 								if(patchbmp != null)
 								{
-									//mxd. Flip
-									if(p.flipx || p.flipy) 
-									{
-										RotateFlipType flip;
-										if(p.flipx && !p.flipy)
-											flip = RotateFlipType.RotateNoneFlipX;
-										else if(!p.flipx && p.flipy)
-											flip = RotateFlipType.RotateNoneFlipY;
-										else
-											flip = RotateFlipType.RotateNoneFlipXY;
-										patchbmp.RotateFlip(flip);
-									}
+									//mxd. Apply transformations from TexturePatch 
+									patchbmp = TransformPatch(p, patchbmp);
 
-									//mxd. Then rotate. I do it this way because RotateFlip function rotates THEN flips, and GZDoom does it the other way around.
-									if(p.rotate != 0) 
-									{
-										RotateFlipType rotate;
-										switch(p.rotate)
-										{
-											case 90: rotate = RotateFlipType.Rotate90FlipNone; break;
-											case 180: rotate = RotateFlipType.Rotate180FlipNone; break;
-											default: rotate = RotateFlipType.Rotate270FlipNone; break;
-										}
-										patchbmp.RotateFlip(rotate);
-									}
-									
-									// Adjust patch alpha, apply tint or blend
-									if(p.blendstyle != TexturePathBlendStyle.None || p.style != TexturePathRenderStyle.Copy)
-									{
-										BitmapData bmpdata = null;
-
-										try
-										{
-											bmpdata = patchbmp.LockBits(new Rectangle(0, 0, patchbmp.Size.Width, patchbmp.Size.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
-										}
-										catch(Exception e)
-										{
-											General.ErrorLogger.Add(ErrorType.Error, "Cannot lock image '" + p.lumpname + "' for alpha adjustment. " + e.GetType().Name + ": " + e.Message);
-										}
-
-										if(bmpdata != null)
-										{
-											PixelColor* pixels = (PixelColor*)(bmpdata.Scan0.ToPointer());
-											int numpixels = bmpdata.Width * bmpdata.Height;
-											int patchalpha = (int)Math.Round(General.Clamp(p.alpha, 0f, 1f) * 255); //convert alpha to [0-255] range
-
-											//mxd. Blend/Tint support
-											if(p.blendstyle == TexturePathBlendStyle.Blend) 
-											{
-												for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--) 
-												{
-													cp->r = (byte)((cp->r * p.blend.r) * PixelColor.BYTE_TO_FLOAT);
-													cp->g = (byte)((cp->g * p.blend.g) * PixelColor.BYTE_TO_FLOAT);
-													cp->b = (byte)((cp->b * p.blend.b) * PixelColor.BYTE_TO_FLOAT);
-												}
-											} 
-											else if(p.blendstyle == TexturePathBlendStyle.Tint) 
-											{
-												float tintammount = p.tintammount - 0.1f;
-
-												if(tintammount > 0) 
-												{
-													float br = p.blend.r * PixelColor.BYTE_TO_FLOAT * tintammount;
-													float bg = p.blend.g * PixelColor.BYTE_TO_FLOAT * tintammount;
-													float bb = p.blend.b * PixelColor.BYTE_TO_FLOAT * tintammount;
-													float invTint = 1.0f - tintammount;
-
-													for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--) 
-													{
-														cp->r = (byte)(((cp->r * PixelColor.BYTE_TO_FLOAT) * invTint + br) * 255.0f);
-														cp->g = (byte)(((cp->g * PixelColor.BYTE_TO_FLOAT) * invTint + bg) * 255.0f);
-														cp->b = (byte)(((cp->b * PixelColor.BYTE_TO_FLOAT) * invTint + bb) * 255.0f);
-													}
-												}
-											}
-											//mxd. apply RenderStyle
-											if(p.style == TexturePathRenderStyle.Blend) 
-											{
-												for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--)
-													cp->a = (byte)((cp->a * patchalpha) * PixelColor.BYTE_TO_FLOAT);
-
-											
-											}
-											//mxd. we need a copy of underlying part of texture for these styles
-											else if(p.style != TexturePathRenderStyle.Copy) 
-											{
-												//copy portion of texture
-												int lockWidth = (p.x + patchbmp.Size.Width > bitmap.Width) ? bitmap.Width - p.x : patchbmp.Size.Width;
-												int lockHeight = (p.y + patchbmp.Size.Height > bitmap.Height) ? bitmap.Height - p.y : patchbmp.Size.Height;
-
-												Bitmap source = new Bitmap(patchbmp.Size.Width, patchbmp.Size.Height);
-												using(Graphics sg = Graphics.FromImage(source))
-													sg.DrawImageUnscaled(bitmap, new Rectangle(-p.x, -p.y, lockWidth, lockHeight));
-												
-												//lock texture
-												BitmapData texturebmpdata = null;
-												
-												try 
-												{
-													texturebmpdata = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
-												} 
-												catch(Exception e) 
-												{
-													General.ErrorLogger.Add(ErrorType.Error, "Cannot lock texture '" + this.Name + "' to apply render style. " + e.GetType().Name + ": " + e.Message);
-												}
-
-												if(texturebmpdata != null) 
-												{
-													PixelColor* texturepixels = (PixelColor*)(texturebmpdata.Scan0.ToPointer());
-													PixelColor* tcp = texturepixels + numpixels - 1;
-
-													switch(p.style) 
-													{
-														case TexturePathRenderStyle.Add:
-															for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--) 
-															{
-																cp->r = (byte)Math.Min(255, cp->r + tcp->r);
-																cp->g = (byte)Math.Min(255, cp->g + tcp->g);
-																cp->b = (byte)Math.Min(255, cp->b + tcp->b);
-																cp->a = (byte)((cp->a * patchalpha) * PixelColor.BYTE_TO_FLOAT);
-																tcp--;
-															}
-															break;
-
-														case TexturePathRenderStyle.Subtract:
-															for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--) 
-															{
-																cp->r = (byte)Math.Max(0, tcp->r - cp->r);
-																cp->g = (byte)Math.Max(0, tcp->g - cp->g);
-																cp->b = (byte)Math.Max(0, tcp->b - cp->b);
-																cp->a = (byte)((cp->a * patchalpha) * PixelColor.BYTE_TO_FLOAT);
-																tcp--;
-															}
-															break;
-
-														case TexturePathRenderStyle.ReverseSubtract:
-															for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--) 
-															{
-																cp->r = (byte)Math.Max(0, cp->r - tcp->r);
-																cp->g = (byte)Math.Max(0, cp->g - tcp->g);
-																cp->b = (byte)Math.Max(0, cp->b - tcp->b);
-																cp->a = (byte)((cp->a * patchalpha) * PixelColor.BYTE_TO_FLOAT);
-																tcp--;
-															}
-															break;
-
-														case TexturePathRenderStyle.Modulate:
-															for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--) 
-															{
-																cp->r = (byte)((cp->r * tcp->r) * PixelColor.BYTE_TO_FLOAT);
-																cp->g = (byte)((cp->g * tcp->g) * PixelColor.BYTE_TO_FLOAT);
-																cp->b = (byte)((cp->b * tcp->b) * PixelColor.BYTE_TO_FLOAT);
-
-																tcp--;
-															}
-															break;
-
-													}
-
-													source.UnlockBits(texturebmpdata);
-												}
-											}
-											patchbmp.UnlockBits(bmpdata);
-										}
-									}
-									
 									// Draw the patch on the texture image
 									Rectangle tgtrect = new Rectangle(p.x, p.y, patchbmp.Size.Width, patchbmp.Size.Height);
 									g.DrawImageUnscaledAndClipped(patchbmp, tgtrect);
@@ -357,15 +196,35 @@ namespace CodeImp.DoomBuilder.Data
 						}
 						else
 						{
+							//mxd. ZDoom can use any known graphic as patch
+							if(General.Map.Config.MixTexturesFlats)
+							{
+								ImageData img = General.Map.Data.GetTextureImage(p.lumpname);
+								if(!(img is UnknownImage))
+								{
+									if(!img.IsImageLoaded) img.LoadImage();
+
+									//mxd. Apply transformations from TexturePatch. We don't want to modify the original bitmap here, so make a copy
+									Bitmap patchbmp = TransformPatch(p, new Bitmap(img.GetBitmap()));
+
+									// Draw the patch on the texture image
+									Rectangle tgtrect = new Rectangle(p.x, p.y, patchbmp.Size.Width, patchbmp.Size.Height);
+									g.DrawImageUnscaledAndClipped(patchbmp, tgtrect);
+									patchbmp.Dispose();
+
+									continue;
+								}
+							}
+							
 							// Missing a patch lump!
 							General.ErrorLogger.Add(ErrorType.Error, "Missing patch lump '" + p.lumpname + "' while loading texture '" + this.Name + "'");
-							failCount++; //mxd
+							missingpatches++; //mxd
 						}
 					}
 				}
 				
 				// Dispose bitmap if load failed
-				if((bitmap != null) && (loadfailed || failCount >= patches.Count)) //mxd. We can still display texture if at least one of the patches was loaded
+				if((bitmap != null) && (loadfailed || missingpatches >= patches.Count)) //mxd. We can still display texture if at least one of the patches was loaded
 				{
 					bitmap.Dispose();
 					bitmap = null;
@@ -377,6 +236,183 @@ namespace CodeImp.DoomBuilder.Data
 			}
 		}
 
+		//mxd
+		private Bitmap TransformPatch(TexturePatch p, Bitmap patchbmp)
+		{
+			//mxd. Flip
+			if(p.flipx || p.flipy)
+			{
+				RotateFlipType flip;
+				if(p.flipx && !p.flipy)
+					flip = RotateFlipType.RotateNoneFlipX;
+				else if(!p.flipx && p.flipy)
+					flip = RotateFlipType.RotateNoneFlipY;
+				else
+					flip = RotateFlipType.RotateNoneFlipXY;
+				patchbmp.RotateFlip(flip);
+			}
+
+			//mxd. Then rotate. I do it this way because RotateFlip function rotates THEN flips, and GZDoom does it the other way around.
+			if(p.rotate != 0)
+			{
+				RotateFlipType rotate;
+				switch(p.rotate)
+				{
+					case 90:
+						rotate = RotateFlipType.Rotate90FlipNone;
+						break;
+					case 180:
+						rotate = RotateFlipType.Rotate180FlipNone;
+						break;
+					default:
+						rotate = RotateFlipType.Rotate270FlipNone;
+						break;
+				}
+				patchbmp.RotateFlip(rotate);
+			}
+
+			// Adjust patch alpha, apply tint or blend
+			if(p.blendstyle != TexturePathBlendStyle.None || p.style != TexturePathRenderStyle.Copy)
+			{
+				BitmapData bmpdata = null;
+
+				try
+				{
+					bmpdata = patchbmp.LockBits(new Rectangle(0, 0, patchbmp.Size.Width, patchbmp.Size.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
+				}
+				catch(Exception e)
+				{
+					General.ErrorLogger.Add(ErrorType.Error, "Cannot lock image '" + p.lumpname + "' for alpha adjustment. " + e.GetType().Name + ": " + e.Message);
+				}
+
+				if(bmpdata != null)
+				{
+					PixelColor* pixels = (PixelColor*)(bmpdata.Scan0.ToPointer());
+					int numpixels = bmpdata.Width * bmpdata.Height;
+					int patchalpha = (int)Math.Round(General.Clamp(p.alpha, 0f, 1f) * 255); //convert alpha to [0-255] range
+
+					//mxd. Blend/Tint support
+					if(p.blendstyle == TexturePathBlendStyle.Blend)
+					{
+						for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--)
+						{
+							cp->r = (byte)((cp->r * p.blend.r) * PixelColor.BYTE_TO_FLOAT);
+							cp->g = (byte)((cp->g * p.blend.g) * PixelColor.BYTE_TO_FLOAT);
+							cp->b = (byte)((cp->b * p.blend.b) * PixelColor.BYTE_TO_FLOAT);
+						}
+					}
+					else if(p.blendstyle == TexturePathBlendStyle.Tint)
+					{
+						float tintammount = p.tintammount - 0.1f;
+
+						if(tintammount > 0)
+						{
+							float br = p.blend.r * PixelColor.BYTE_TO_FLOAT * tintammount;
+							float bg = p.blend.g * PixelColor.BYTE_TO_FLOAT * tintammount;
+							float bb = p.blend.b * PixelColor.BYTE_TO_FLOAT * tintammount;
+							float invTint = 1.0f - tintammount;
+
+							for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--)
+							{
+								cp->r = (byte)(((cp->r * PixelColor.BYTE_TO_FLOAT) * invTint + br) * 255.0f);
+								cp->g = (byte)(((cp->g * PixelColor.BYTE_TO_FLOAT) * invTint + bg) * 255.0f);
+								cp->b = (byte)(((cp->b * PixelColor.BYTE_TO_FLOAT) * invTint + bb) * 255.0f);
+							}
+						}
+					}
+
+					//mxd. Apply RenderStyle
+					if(p.style == TexturePathRenderStyle.Blend)
+					{
+						for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--)
+							cp->a = (byte)((cp->a * patchalpha) * PixelColor.BYTE_TO_FLOAT);
+					}
+					//mxd. We need a copy of underlying part of texture for these styles
+					else if(p.style != TexturePathRenderStyle.Copy)
+					{
+						// Copy portion of texture
+						int lockWidth = (p.x + patchbmp.Size.Width > bitmap.Width) ? bitmap.Width - p.x : patchbmp.Size.Width;
+						int lockHeight = (p.y + patchbmp.Size.Height > bitmap.Height) ? bitmap.Height - p.y : patchbmp.Size.Height;
+
+						Bitmap source = new Bitmap(patchbmp.Size.Width, patchbmp.Size.Height);
+						using(Graphics sg = Graphics.FromImage(source))
+							sg.DrawImageUnscaled(bitmap, new Rectangle(-p.x, -p.y, lockWidth, lockHeight));
+
+						// Lock texture
+						BitmapData texturebmpdata = null;
+
+						try
+						{
+							texturebmpdata = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
+						}
+						catch(Exception e)
+						{
+							General.ErrorLogger.Add(ErrorType.Error, "Cannot lock texture '" + this.Name + "' to apply render style. " + e.GetType().Name + ": " + e.Message);
+						}
+
+						if(texturebmpdata != null)
+						{
+							PixelColor* texturepixels = (PixelColor*)(texturebmpdata.Scan0.ToPointer());
+							PixelColor* tcp = texturepixels + numpixels - 1;
+
+							switch(p.style)
+							{
+								case TexturePathRenderStyle.Add:
+									for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--)
+									{
+										cp->r = (byte)Math.Min(255, cp->r + tcp->r);
+										cp->g = (byte)Math.Min(255, cp->g + tcp->g);
+										cp->b = (byte)Math.Min(255, cp->b + tcp->b);
+										cp->a = (byte)((cp->a * patchalpha) * PixelColor.BYTE_TO_FLOAT);
+										tcp--;
+									}
+									break;
+
+								case TexturePathRenderStyle.Subtract:
+									for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--)
+									{
+										cp->r = (byte)Math.Max(0, tcp->r - cp->r);
+										cp->g = (byte)Math.Max(0, tcp->g - cp->g);
+										cp->b = (byte)Math.Max(0, tcp->b - cp->b);
+										cp->a = (byte)((cp->a * patchalpha) * PixelColor.BYTE_TO_FLOAT);
+										tcp--;
+									}
+									break;
+
+								case TexturePathRenderStyle.ReverseSubtract:
+									for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--)
+									{
+										cp->r = (byte)Math.Max(0, cp->r - tcp->r);
+										cp->g = (byte)Math.Max(0, cp->g - tcp->g);
+										cp->b = (byte)Math.Max(0, cp->b - tcp->b);
+										cp->a = (byte)((cp->a * patchalpha) * PixelColor.BYTE_TO_FLOAT);
+										tcp--;
+									}
+									break;
+
+								case TexturePathRenderStyle.Modulate:
+									for(PixelColor* cp = pixels + numpixels - 1; cp >= pixels; cp--)
+									{
+										cp->r = (byte)((cp->r * tcp->r) * PixelColor.BYTE_TO_FLOAT);
+										cp->g = (byte)((cp->g * tcp->g) * PixelColor.BYTE_TO_FLOAT);
+										cp->b = (byte)((cp->b * tcp->b) * PixelColor.BYTE_TO_FLOAT);
+										tcp--;
+									}
+									break;
+
+							}
+
+							source.UnlockBits(texturebmpdata);
+						}
+					}
+
+					patchbmp.UnlockBits(bmpdata);
+				}
+			}
+
+			return patchbmp;
+		}
+
 		#endregion
 	}
 }
diff --git a/Source/Core/Editing/ThingsFilter.cs b/Source/Core/Editing/ThingsFilter.cs
index d8a2e5535..872518e31 100644
--- a/Source/Core/Editing/ThingsFilter.cs
+++ b/Source/Core/Editing/ThingsFilter.cs
@@ -272,7 +272,7 @@ namespace CodeImp.DoomBuilder.Editing
 
 			//Integrity check
 			if(!IsValid())
-				General.ErrorLogger.Add(ErrorType.Warning, "Things filter '" + name + "' has invalid properties. Configure the thing filter to fix this!");
+				General.ErrorLogger.Add(ErrorType.Warning, "Things filter '" + name + "' has invalid properties. Configure the things filter to fix this!");
 		}
 
 		//mxd
-- 
GitLab