diff --git a/DesignTools/ImageAsset.cs b/DesignTools/ImageAsset.cs
deleted file mode 100644
index ab8eb43ed..000000000
--- a/DesignTools/ImageAsset.cs
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
-Copyright (c) 2018, Lars Brubaker, John Lewin
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-The views and conclusions contained in the software and documentation are those
-of the authors and should not be interpreted as representing official policies,
-either expressed or implied, of the FreeBSD Project.
-*/
-
-using System.IO;
-using MatterHackers.Agg.Image;
-using MatterHackers.Agg.ImageProcessing;
-using MatterHackers.Agg.Platform;
-using Newtonsoft.Json;
-
-namespace MatterHackers.MatterControl.DesignTools
-{
- public class ImageAsset
- {
- private string _assetPath;
- private ImageBuffer _image;
- private bool _invert;
-
- public string AssetPath
- {
- get { return _assetPath; }
- set
- {
- if (_assetPath != value)
- {
- _assetPath = value;
- _image = null;
- }
- }
- }
-
- [JsonIgnore]
- public ImageBuffer Image
- {
- get
- {
- if (_image == null)
- {
- if (File.Exists(AssetPath))
- {
- _image = AggContext.ImageIO.LoadImage(AssetPath);
- if (Invert)
- {
- _image = InvertLightness.DoInvertLightness(_image);
- }
- }
- }
-
- return _image;
- }
- }
-
- public bool Invert
- {
- get { return _invert; }
- set
- {
- if (_invert != value)
- {
- _invert = value;
- _image = null;
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/DesignTools/Primitives/ImageObject3D.cs b/DesignTools/Primitives/ImageObject3D.cs
new file mode 100644
index 000000000..87ab313e8
--- /dev/null
+++ b/DesignTools/Primitives/ImageObject3D.cs
@@ -0,0 +1,184 @@
+/*
+Copyright (c) 2017, Lars Brubaker, John Lewin
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those
+of the authors and should not be interpreted as representing official policies,
+either expressed or implied, of the FreeBSD Project.
+*/
+
+using System;
+using System.IO;
+using System.Threading;
+using MatterHackers.Agg.Image;
+using MatterHackers.Agg.ImageProcessing;
+using MatterHackers.Agg.Platform;
+using MatterHackers.Agg.UI;
+using MatterHackers.DataConverters3D;
+using MatterHackers.PolygonMesh;
+using Newtonsoft.Json;
+
+namespace MatterHackers.MatterControl.DesignTools
+{
+ public class ImageObject3D : AssetObject3D, IRebuildable
+ {
+ private const double DefaultSizeMm = 60;
+
+ private string _assetPath;
+
+ private ImageBuffer _image;
+
+ private bool _invert;
+
+ public ImageObject3D()
+ {
+ base.Mesh = null;
+ }
+
+ public override string AssetPath
+ {
+ get => _assetPath;
+ set
+ {
+ if (_assetPath != value)
+ {
+ _assetPath = value;
+ _image = null;
+ }
+ }
+ }
+
+ [JsonIgnore]
+ public ImageBuffer Image
+ {
+ get
+ {
+ if (_image == null)
+ {
+ if (File.Exists(this.AssetPath))
+ {
+ _image = this.LoadImage();
+
+ if (this.Invert)
+ {
+ _image = InvertLightness.DoInvertLightness(_image);
+ }
+
+ base.Mesh = this.InitMesh() ?? PlatonicSolids.CreateCube(100, 100, 0.2);
+ }
+ }
+
+ return _image;
+ }
+ }
+
+ public bool Invert
+ {
+ get => _invert;
+ set
+ {
+ if (_invert != value)
+ {
+ _invert = value;
+ _image = null;
+
+ this.OnInvalidate();
+ }
+ }
+ }
+
+ private ImageBuffer LoadImage()
+ {
+ // TODO: Consider non-awful alternatives
+ var resetEvent = new AutoResetEvent(false);
+
+ ImageBuffer imageBuffer = null;
+
+ this.LoadAsset(CancellationToken.None, null).ContinueWith((streamTask) =>
+ {
+ Stream assetStream = null;
+ try
+ {
+ assetStream = streamTask.Result;
+ imageBuffer = AggContext.ImageIO.LoadImage(assetStream);
+ }
+ catch { }
+
+ assetStream?.Dispose();
+
+ resetEvent.Set();
+ });
+
+ // Wait up to 30 seconds for a given image asset
+ resetEvent.WaitOne(30 * 1000);
+
+ return imageBuffer;
+ }
+
+ public override Mesh Mesh
+ {
+ get
+ {
+ if (!string.IsNullOrWhiteSpace(this.AssetPath)
+ // TODO: Remove this hack needed to work around Persistable = false
+ && (base.Mesh == null || base.Mesh.FaceTexture.Count <= 0))
+ {
+ // TODO: Revise fallback mesh
+ base.Mesh = this.InitMesh() ?? PlatonicSolids.CreateCube(100, 100, 0.2);
+ }
+
+ return base.Mesh;
+ }
+ }
+
+ public double ScaleMmPerPixels { get; private set; }
+
+ private Mesh InitMesh()
+ {
+ if (!string.IsNullOrWhiteSpace(this.AssetPath))
+ {
+ var imageBuffer = this.Image;
+ if (imageBuffer != null)
+ {
+ ScaleMmPerPixels = Math.Min(DefaultSizeMm / imageBuffer.Width, DefaultSizeMm / imageBuffer.Height);
+
+ // Create texture mesh
+ double width = ScaleMmPerPixels * imageBuffer.Width;
+ double height = ScaleMmPerPixels * imageBuffer.Height;
+
+ Mesh textureMesh = PlatonicSolids.CreateCube(width, height, 0.2);
+ MeshHelper.PlaceTextureOnFace(textureMesh.Faces[0], imageBuffer);
+
+ return textureMesh;
+ }
+ }
+
+ return null;
+ }
+
+ public void Rebuild(UndoBuffer undoBuffer)
+ {
+ //MeshHelper.PlaceTextureOnFace(Mesh.Faces[0], ImageAsset.Image);
+ }
+ }
+}
\ No newline at end of file
diff --git a/DesignTools/Primitives/RingObject3D.cs b/DesignTools/Primitives/RingObject3D.cs
index a6e5ba4ab..6edb1acd0 100644
--- a/DesignTools/Primitives/RingObject3D.cs
+++ b/DesignTools/Primitives/RingObject3D.cs
@@ -65,8 +65,8 @@ namespace MatterHackers.MatterControl.DesignTools
}
public double OuterDiameter { get; set; } = 20;
- public double InnerDiameter { get; set; } = 25;
- public double Height { get; set; } = 20;
+ public double InnerDiameter { get; set; } = 15;
+ public double Height { get; set; } = 5;
public int Sides { get; set; } = 30;
public void Rebuild(UndoBuffer undoBuffer)
diff --git a/DesignTools/PublicPropertyEditor.cs b/DesignTools/PublicPropertyEditor.cs
index 524ce9685..23d7ef165 100644
--- a/DesignTools/PublicPropertyEditor.cs
+++ b/DesignTools/PublicPropertyEditor.cs
@@ -62,7 +62,7 @@ namespace MatterHackers.MatterControl.DesignTools
typeof(double), typeof(int), typeof(char), typeof(string), typeof(bool),
typeof(Vector2), typeof(Vector3),
typeof(DirectionVector), typeof(DirectionAxis),
- typeof(ImageAsset)
+ typeof(ImageObject3D)
};
public const BindingFlags OwnedPropertiesOnly = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
@@ -458,12 +458,10 @@ namespace MatterHackers.MatterControl.DesignTools
editControlsContainer.AddChild(rowContainer);
}
// create an image asset editor
- else if (property.Value is ImageAsset imageAsset)
+ else if (property.Value is ImageObject3D imageObject)
{
- rowContainer = CreateImageEditor(rebuildable,
- imageAsset,
- theme,
- undoBuffer);
+ var editor = new ImageEditor();
+ rowContainer = editor.Create(imageObject, view3DWidget, theme);
editControlsContainer.AddChild(rowContainer);
}
@@ -526,158 +524,6 @@ namespace MatterHackers.MatterControl.DesignTools
}
}
- private GuiWidget CreateImageEditor(IRebuildable item,
- ImageAsset imageAsset,
- ThemeConfig theme,
- UndoBuffer undoBuffer)
- {
- var column = new FlowLayoutWidget(FlowDirection.TopToBottom)
- {
- HAnchor = HAnchor.Stretch
- };
-
- var imageSection = new SectionWidget(
- "Image".Localize(),
- new FlowLayoutWidget(FlowDirection.TopToBottom),
- theme).ApplyBoxStyle(margin: 0);
-
- column.AddChild(imageSection);
-
- ImageBuffer thumbnailImage = SetImage(theme, imageAsset);
-
- ImageWidget thumbnailWidget;
- imageSection.ContentPanel.AddChild(thumbnailWidget = new ImageWidget(thumbnailImage)
- {
- Margin = new BorderDouble(bottom: 5),
- HAnchor = HAnchor.Center
- });
-
- // add a search Google box
- var searchRow = new FlowLayoutWidget()
- {
- HAnchor = HAnchor.Stretch,
- VAnchor = VAnchor.Fit,
- };
- imageSection.ContentPanel.AddChild(searchRow);
-
- MHTextEditWidget textToAddWidget = new MHTextEditWidget("", pixelWidth: 300, messageWhenEmptyAndNotSelected: "Search Google".Localize());
- textToAddWidget.HAnchor = HAnchor.Stretch;
- searchRow.AddChild(textToAddWidget);
- textToAddWidget.ActualTextEditWidget.EnterPressed += (object sender, KeyEventArgs keyEvent) =>
- {
- UiThread.RunOnIdle(() =>
- {
- textToAddWidget.Unfocus();
- string search = "http://www.google.com/search?q={0} silhouette&tbm=isch".FormatWith(textToAddWidget.Text);
- ApplicationController.Instance.LaunchBrowser(search);
- });
- };
-
- // add in the invert checkbox and change image button
- var changeImageButton = new TextButton("Change".Localize(), theme)
- {
- BackgroundColor = theme.MinimalShade
- };
- changeImageButton.Click += (sender, e) =>
- {
- UiThread.RunOnIdle(() =>
- {
- // we do this using to make sure that the stream is closed before we try and insert the Picture
- AggContext.FileDialogs.OpenFileDialog(
- new OpenFileDialogParams(
- "Select an image file|*.jpg;*.png;*.bmp;*.gif;*.pdf",
- multiSelect: false,
- title: "Add Image".Localize()),
- (openParams) =>
- {
- if (!File.Exists(openParams.FileName))
- {
- return;
- }
-
- imageAsset.AssetPath = openParams.FileName;
- thumbnailWidget.Image = SetImage(theme, imageAsset);
-
- item?.Rebuild(undoBuffer);
-
- column.Invalidate();
- });
- });
- };
-
- var row = new FlowLayoutWidget()
- {
- HAnchor = HAnchor.Stretch,
- VAnchor = VAnchor.Fit,
- };
- imageSection.ContentPanel.AddChild(row);
-
- // Invert checkbox
- var invertCheckbox = new CheckBox(new CheckBoxViewText("Invert".Localize(), textColor: theme.Colors.PrimaryTextColor))
- {
- Checked = imageAsset.Invert,
- Margin = new BorderDouble(0),
- };
- invertCheckbox.CheckedStateChanged += (s, e) =>
- {
- imageAsset.Invert = invertCheckbox.Checked;
- thumbnailWidget.Image = SetImage(theme, imageAsset);
- item?.Rebuild(undoBuffer);
- };
- row.AddChild(invertCheckbox);
-
- row.AddChild(new HorizontalSpacer());
-
- row.AddChild(changeImageButton);
-
- return column;
- }
-
- private ImageBuffer SetImage(ThemeConfig theme, ImageAsset imageAsset)
- {
- var image = imageAsset.Image;
- // Show image load error if needed
- if (image == null)
- {
- image = new ImageBuffer(185, 185).SetPreMultiply();
- var graphics2D = image.NewGraphics2D();
-
- graphics2D.FillRectangle(0, 0, 185, 185, theme.MinimalShade);
- graphics2D.Rectangle(0, 0, 185, 185, theme.SlightShade);
- graphics2D.DrawString("Error Loading Image".Localize() + "...", 10, 185 / 2, baseline: Agg.Font.Baseline.BoundsCenter, color: Color.Red, pointSize: theme.DefaultFontSize, drawFromHintedCach: true);
- }
-
- return (image.Height <= 185) ? image : ScaleThumbnailImage(185, image);
- }
-
- private ImageBuffer ScaleThumbnailImage(int height, ImageBuffer imageBuffer)
- {
- if (imageBuffer.Height != height)
- {
- var factor = (double)height / imageBuffer.Height;
-
- int width = (int)(imageBuffer.Width * factor);
-
- var scaledImageBuffer = new ImageBuffer(width, height);
- scaledImageBuffer.NewGraphics2D().Render(imageBuffer, 0, 0, width, height);
- return scaledImageBuffer;
- }
-
- return imageBuffer;
- }
-
- private ImageBuffer ScaleThumbnailImage(int width, int height, ImageBuffer imageBuffer)
- {
- if (imageBuffer.Width != width)
- {
- var scaledImageBuffer = new ImageBuffer(width, height);
- scaledImageBuffer.NewGraphics2D().Render(imageBuffer, 0, 0, scaledImageBuffer.Width, scaledImageBuffer.Height);
- imageBuffer = scaledImageBuffer;
- }
-
- return imageBuffer;
- }
-
private GuiWidget CreateEnumEditor(IRebuildable item,
PropertyInfo propertyInfo, Type propertyType, object value, string displayName,
ThemeConfig theme,
diff --git a/MatterControl.csproj b/MatterControl.csproj
index e5a6c7e17..428fa0856 100644
--- a/MatterControl.csproj
+++ b/MatterControl.csproj
@@ -91,7 +91,6 @@
-
@@ -107,6 +106,7 @@
+
@@ -135,6 +135,7 @@
+
diff --git a/PartPreviewWindow/View3D/Actions/ImageEditor.cs b/PartPreviewWindow/View3D/Actions/ImageEditor.cs
new file mode 100644
index 000000000..d04bdcb29
--- /dev/null
+++ b/PartPreviewWindow/View3D/Actions/ImageEditor.cs
@@ -0,0 +1,222 @@
+/*
+Copyright (c) 2018, Lars Brubaker, John Lewin
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those
+of the authors and should not be interpreted as representing official policies,
+either expressed or implied, of the FreeBSD Project.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using MatterHackers.Agg;
+using MatterHackers.Agg.Image;
+using MatterHackers.Agg.UI;
+using MatterHackers.Localizations;
+using MatterHackers.MatterControl.PartPreviewWindow;
+
+namespace MatterHackers.MatterControl.DesignTools
+{
+ using CustomWidgets;
+ using DataConverters3D;
+ using MatterHackers.Agg.Platform;
+ using MatterHackers.MatterControl.Library;
+
+ public class ImageEditor : IObject3DEditor
+ {
+ bool IObject3DEditor.Unlocked => true;
+
+ string IObject3DEditor.Name => "Image Editor";
+
+ public GuiWidget Create(IObject3D item, View3DWidget parentView3D, ThemeConfig theme)
+ {
+ var column = new FlowLayoutWidget(FlowDirection.TopToBottom)
+ {
+ HAnchor = HAnchor.MaxFitOrStretch
+ };
+
+ var imageObject = item as ImageObject3D;
+
+ var activeImage = imageObject.Image;
+
+ var imageSection = new SectionWidget(
+ "Image".Localize(),
+ new FlowLayoutWidget(FlowDirection.TopToBottom),
+ theme).ApplyBoxStyle(margin: 0);
+
+ column.AddChild(imageSection);
+
+ ImageBuffer thumbnailImage = SetImage(theme, imageObject);
+
+ ImageWidget thumbnailWidget;
+ imageSection.ContentPanel.AddChild(thumbnailWidget = new ImageWidget(thumbnailImage)
+ {
+ Margin = new BorderDouble(bottom: 5),
+ HAnchor = HAnchor.Center
+ });
+
+ bool addImageSearch = false;
+ if(addImageSearch)
+ {
+ // add a search Google box
+ var searchRow = new FlowLayoutWidget()
+ {
+ HAnchor = HAnchor.Stretch,
+ VAnchor = VAnchor.Fit,
+ };
+ imageSection.ContentPanel.AddChild(searchRow);
+
+ MHTextEditWidget textToAddWidget = new MHTextEditWidget("", pixelWidth: 300, messageWhenEmptyAndNotSelected: "Search Google".Localize());
+ textToAddWidget.HAnchor = HAnchor.Stretch;
+ searchRow.AddChild(textToAddWidget);
+ textToAddWidget.ActualTextEditWidget.EnterPressed += (object sender, KeyEventArgs keyEvent) =>
+ {
+ UiThread.RunOnIdle(() =>
+ {
+ textToAddWidget.Unfocus();
+ string search = "http://www.google.com/search?q={0} silhouette&tbm=isch".FormatWith(textToAddWidget.Text);
+ ApplicationController.Instance.LaunchBrowser(search);
+ });
+ };
+ }
+
+ // add in the invert checkbox and change image button
+ var addButton = new TextButton("Change".Localize(), theme)
+ {
+ BackgroundColor = theme.MinimalShade
+ };
+ addButton.Click += (sender, e) =>
+ {
+ UiThread.RunOnIdle(() =>
+ {
+ // we do this using to make sure that the stream is closed before we try and insert the Picture
+ AggContext.FileDialogs.OpenFileDialog(
+ new OpenFileDialogParams(
+ "Select an image file|*.jpg;*.png;*.bmp;*.gif;*.pdf",
+ multiSelect: false,
+ title: "Add Image".Localize()),
+ (openParams) =>
+ {
+ if (!File.Exists(openParams.FileName))
+ {
+ return;
+ }
+
+ imageObject.AssetPath = openParams.FileName;
+ imageObject.Mesh = null;
+
+ thumbnailWidget.Image = SetImage(theme, imageObject);
+
+ column.Invalidate();
+ imageObject.Invalidate();
+ });
+ });
+ };
+
+ var row = new FlowLayoutWidget()
+ {
+ HAnchor = HAnchor.Stretch,
+ VAnchor = VAnchor.Fit,
+ };
+ imageSection.ContentPanel.AddChild(row);
+
+ // Invert checkbox
+ var invertCheckbox = new CheckBox(new CheckBoxViewText("Invert".Localize(), textColor: theme.Colors.PrimaryTextColor))
+ {
+ Checked = imageObject.Invert,
+ Margin = new BorderDouble(0),
+ };
+ invertCheckbox.CheckedStateChanged += (s, e) =>
+ {
+ imageObject.Invert = invertCheckbox.Checked;
+ };
+ row.AddChild(invertCheckbox);
+
+ row.AddChild(new HorizontalSpacer());
+
+ row.AddChild(addButton);
+
+ imageObject.Invalidated += (s, e) =>
+ {
+ if (activeImage != imageObject.Image)
+ {
+ thumbnailImage = SetImage(theme, imageObject);
+ thumbnailWidget.Image = thumbnailImage;
+
+ activeImage = imageObject.Image;
+ }
+ };
+
+ return column;
+ }
+
+ private ImageBuffer SetImage(ThemeConfig theme, ImageObject3D imageObject)
+ {
+ var image = imageObject.Image;
+ // Show image load error if needed
+ if (image == null)
+ {
+ image = new ImageBuffer(185, 185).SetPreMultiply();
+ var graphics2D = image.NewGraphics2D();
+
+ graphics2D.FillRectangle(0, 0, 185, 185, theme.MinimalShade);
+ graphics2D.Rectangle(0, 0, 185, 185, theme.SlightShade);
+ graphics2D.DrawString("Error Loading Image".Localize() + "...", 10, 185 / 2, baseline: Agg.Font.Baseline.BoundsCenter, color: Color.Red, pointSize: theme.DefaultFontSize, drawFromHintedCach: true);
+ }
+
+ return (image.Height <= 185) ? image : ScaleThumbnailImage(185, image);
+ }
+
+ IEnumerable IObject3DEditor.SupportedTypes() => new[] { typeof(ImageObject3D) };
+
+ private ImageBuffer ScaleThumbnailImage(int height, ImageBuffer imageBuffer)
+ {
+ if (imageBuffer.Height != height)
+ {
+ var factor = (double) height / imageBuffer.Height;
+
+ int width = (int)(imageBuffer.Width * factor);
+
+ var scaledImageBuffer = new ImageBuffer(width, height);
+ scaledImageBuffer.NewGraphics2D().Render(imageBuffer, 0, 0, width, height);
+ return scaledImageBuffer;
+ }
+
+ return imageBuffer;
+ }
+
+ private ImageBuffer ScaleThumbnailImage(int width, int height, ImageBuffer imageBuffer)
+ {
+ if (imageBuffer.Width != width)
+ {
+ var scaledImageBuffer = new ImageBuffer(width, height);
+ scaledImageBuffer.NewGraphics2D().Render(imageBuffer, 0, 0, scaledImageBuffer.Width, scaledImageBuffer.Height);
+ imageBuffer = scaledImageBuffer;
+ }
+
+ return imageBuffer;
+ }
+
+ }
+}