From de2117f9bf2c091f4870933d364c5d29469b9f91 Mon Sep 17 00:00:00 2001
From: Starbeamrainbowlabs <sbrl@starbeamrainbowlabs.com>
Date: Thu, 14 Jul 2016 21:25:58 +0100
Subject: [PATCH] Send error images in case of exception. Also make image
 sending code more robust.

---
 GalleryShare/GalleryServer.cs      |  3 +-
 GalleryShare/ThumbnailGenerator.cs | 58 +++++++++++++++++++++++++++---
 2 files changed, 55 insertions(+), 6 deletions(-)

diff --git a/GalleryShare/GalleryServer.cs b/GalleryShare/GalleryServer.cs
index fc32732..b981482 100644
--- a/GalleryShare/GalleryServer.cs
+++ b/GalleryShare/GalleryServer.cs
@@ -227,8 +227,7 @@ namespace GalleryShare
 			{
 				// Send a thumbnail!
 				Console.WriteLine("Sending thumbnail for '{0}'", requestedPath);
-				cycle.Response.ContentType = "image/png";
-				ThumbnailGenerator.GenerateThumbnailPng(requestedPath, thumbnailSize, cycle.Response.OutputStream);
+				await ThumbnailGenerator.SendThumbnailPng(requestedPath, thumbnailSize, cycle);
 				return;
 			}
 
diff --git a/GalleryShare/ThumbnailGenerator.cs b/GalleryShare/ThumbnailGenerator.cs
index 70c80e4..83ed5e5 100644
--- a/GalleryShare/ThumbnailGenerator.cs
+++ b/GalleryShare/ThumbnailGenerator.cs
@@ -5,13 +5,16 @@ using System.IO;
 
 using System.Drawing.Drawing2D;
 using System.Drawing.Imaging;
+using System.Runtime.InteropServices;
+using System.Net;
 
 namespace GalleryShare
 {
 	public static class ThumbnailGenerator
 	{
-		public static void GenerateThumbnailPng(string imagePath, Size thumbnailBounds, Stream outputStream)
+		public static async Task SendThumbnailPng(string imagePath, Size thumbnailBounds, HttpListenerContext cycle)
 		{
+			Image imageToSend;
 			try {
 				using (Bitmap rawImage = new Bitmap(imagePath)) {
 					float scaleFactor = Math.Min(
@@ -23,7 +26,7 @@ namespace GalleryShare
 						(int)(rawImage.Height * scaleFactor)
 					);
 					
-					using (Bitmap smallImage = new Bitmap(thumbnailSize.Width, thumbnailSize.Height))
+					Bitmap smallImage = new Bitmap(thumbnailSize.Width, thumbnailSize.Height);
 					using (Graphics context = Graphics.FromImage(smallImage)) {
 						context.CompositingMode = CompositingMode.SourceCopy;
 						context.InterpolationMode = InterpolationMode.HighQualityBicubic;
@@ -32,13 +35,60 @@ namespace GalleryShare
 							thumbnailSize
 						));
 						
-						smallImage.Save(outputStream, ImageFormat.Png);
+						//smallImage.Save(outputStream, ImageFormat.Png);
+						imageToSend = smallImage;
 					}
 				}
 			}
 			catch (Exception error)
 			{
-				throw new NotImplementedException("Error images haven't been implemented yet.", error);
+				Console.WriteLine("[{0}] Error generating thumbnail {1}: {2}",
+					DateTime.Now.ToShortTimeString(),
+					imagePath,
+					error.Message
+				);
+
+				using (Font drawingFont = new Font("Sans-serif", 18f, FontStyle.Regular, GraphicsUnit.Pixel))
+				{
+					imageToSend = GenerateErrorImage(error.Message, drawingFont, Color.DarkGray, Color.Transparent);
+				}
+			}
+
+			byte[] imageData = GetImageBytesPng(imageToSend);
+
+			cycle.Response.ContentType = "image/png";
+			cycle.Response.ContentLength64 = imageData.LongLength;
+
+			await cycle.Response.OutputStream.WriteAsync(imageData, 0, imageData.Length);
+
+			imageToSend.Dispose();
+		}
+
+		public static Image GenerateErrorImage(string errorText, Font textFont, Color foregroundColour, Color backgroundColour)
+		{
+			SizeF textSize = SizeF.Empty;
+			using(Image measuringImage = new Bitmap(1, 1))
+			using(Graphics measuringContext = Graphics.FromImage(measuringImage))
+			{
+				textSize = measuringContext.MeasureString(errorText, textFont);
+			}
+
+			Image resultImage = new Bitmap((int)textSize.Width, (int)textSize.Height);
+			using (Graphics resultContext = Graphics.FromImage(resultImage))
+			using (SolidBrush drawingBrush = new SolidBrush(foregroundColour))
+			{
+				resultContext.Clear(backgroundColour);
+				resultContext.DrawString(errorText, textFont, drawingBrush, 0f, 0f);
+			}
+			return resultImage;
+		}
+
+		public static byte[] GetImageBytesPng(Image sourceImage)
+		{
+			using (MemoryStream mStream = new MemoryStream())
+			{
+				sourceImage.Save(mStream, ImageFormat.Png);
+				return mStream.ToArray();
 			}
 		}
 	}
