/// <summary>
        /// Function to render the image data into an 32 bit RGBA pixel format render target and then return it as the properly formatted image data.
        /// </summary>
        /// <param name="texture">The texture to render.</param>
        /// <returns>The converted image data for the texture.</returns>
        private IGorgonImage RenderImageData(GorgonTexture2DView texture)
        {
            GorgonRenderTargetView   oldRtv        = _graphics.RenderTargets[0];
            GorgonRenderTarget2DView convertTarget = null;
            GorgonTexture2DView      rtvTexture    = null;
            IGorgonImage             tempImage     = null;
            BufferFormat             targetFormat  = texture.FormatInformation.IsSRgb ? BufferFormat.R8G8B8A8_UNorm_SRgb : BufferFormat.R8G8B8A8_UNorm;

            try
            {
                IGorgonImage resultImage = new GorgonImage(new GorgonImageInfo(ImageType.Image2D, targetFormat)
                {
                    Width      = texture.Width,
                    Height     = texture.Height,
                    ArrayCount = texture.ArrayCount
                });

                convertTarget = GorgonRenderTarget2DView.CreateRenderTarget(_graphics, new GorgonTexture2DInfo(texture, "Convert_rtv")
                {
                    ArrayCount = 1,
                    MipLevels  = 1,
                    Format     = targetFormat,
                    Binding    = TextureBinding.ShaderResource
                });

                rtvTexture = convertTarget.GetShaderResourceView();

                for (int i = 0; i < texture.ArrayCount; ++i)
                {
                    convertTarget.Clear(GorgonColor.BlackTransparent);
                    _graphics.SetRenderTarget(convertTarget);
                    _renderer.Begin();
                    _renderer.DrawFilledRectangle(new DX.RectangleF(0, 0, texture.Width, texture.Height),
                                                  GorgonColor.White,
                                                  texture,
                                                  new DX.RectangleF(0, 0, 1, 1),
                                                  i);
                    _renderer.End();

                    tempImage?.Dispose();
                    tempImage = convertTarget.Texture.ToImage();
                    tempImage.Buffers[0].CopyTo(resultImage.Buffers[0, i]);
                }

                return(resultImage);
            }
            finally
            {
                tempImage?.Dispose();
                _graphics.SetRenderTarget(oldRtv);
                rtvTexture?.Dispose();
                convertTarget?.Dispose();
            }
        }
Esempio n. 2
0
        /// <summary>Handles the RenderToBitmap event of the PanelDisplay control.</summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="PaintEventArgs"/> instance containing the event data.</param>
        private void PanelDisplay_RenderToBitmap(object sender, PaintEventArgs e)
        {
            if ((IsDesignTime) || (_swapChain == null))
            {
                return;
            }

            // This method is used to capture the D3D rendering when rendering to a GDI+ bitmap (as used by the overlay).
            // Without it, no image is rendered and only a dark grey background is visible.

            IGorgonImage swapBufferImage = null;
            Bitmap       gdiBitmap       = null;

            try
            {
                RenderImage();

                swapBufferImage = _swapChain.CopyBackBufferToImage();
                gdiBitmap       = swapBufferImage.Buffers[0].ToBitmap();
                swapBufferImage.Dispose();

                e.Graphics.DrawImage(gdiBitmap, new Point(0, 0));
            }
            catch
            {
                // Empty on purpose.  Don't need to worry about exceptions here, if things fail, they fail and state should not be corrupted.
            }
            finally
            {
                gdiBitmap?.Dispose();
                swapBufferImage?.Dispose();
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Function to render the thumbnail into the image passed in.
        /// </summary>
        /// <param name="image">The image to render the thumbnail into.</param>
        /// <param name="scale">The scale of the image.</param>
        private void RenderThumbnail(ref IGorgonImage image, float scale)
        {
            lock (_syncLock)
            {
                using (GorgonTexture2D texture = image.ToTexture2D(GraphicsContext.Graphics, new GorgonTexture2DLoadOptions
                {
                    Usage = ResourceUsage.Immutable,
                    IsTextureCube = false
                }))
                    using (var rtv = GorgonRenderTarget2DView.CreateRenderTarget(GraphicsContext.Graphics, new GorgonTexture2DInfo
                    {
                        ArrayCount = 1,
                        Binding = TextureBinding.ShaderResource,
                        Format = BufferFormat.R8G8B8A8_UNorm,
                        MipLevels = 1,
                        Height = (int)(image.Height * scale),
                        Width = (int)(image.Width * scale),
                        Usage = ResourceUsage.Default
                    }))
                    {
                        GorgonTexture2DView view = texture.GetShaderResourceView(mipCount: 1, arrayCount: 1);
                        rtv.Clear(GorgonColor.BlackTransparent);
                        GraphicsContext.Graphics.SetRenderTarget(rtv);
                        GraphicsContext.Graphics.DrawTexture(view, new DX.Rectangle(0, 0, rtv.Width, rtv.Height), blendState: GorgonBlendState.Default, samplerState: GorgonSamplerState.Default);
                        GraphicsContext.Graphics.SetRenderTarget(null);

                        image?.Dispose();
                        image = rtv.Texture.ToImage();
                    }
            }
        }
        /// <summary>
        /// Function to retrieve the image data for a sprite texture as a 32 bit RGBA pixel data.
        /// </summary>
        /// <param name="data">The data used for sprite extraction.</param>
        /// <returns>The image data for the texture.</returns>
        public async Task <IGorgonImage> GetSpriteTextureImageDataAsync(SpriteExtractionData data)
        {
            IGorgonImage imageData = data.Texture.Texture.ToImage();

            if ((imageData.Format == BufferFormat.R8G8B8A8_UNorm) ||
                (imageData.Format == BufferFormat.R8G8B8A8_UNorm_SRgb))
            {
                return(imageData);
            }

            if ((imageData.FormatInfo.IsSRgb) &&
                (imageData.CanConvertToFormat(BufferFormat.R8G8B8A8_UNorm_SRgb)))
            {
                await Task.Run(() => imageData.ConvertToFormat(BufferFormat.R8G8B8A8_UNorm_SRgb));

                return(imageData);
            }
            else if (imageData.CanConvertToFormat(BufferFormat.R8G8B8A8_UNorm))
            {
                await Task.Run(() => imageData.ConvertToFormat(BufferFormat.R8G8B8A8_UNorm));

                return(imageData);
            }

            // OK, so this is going to take drastic measures.
            imageData.Dispose();
            return(RenderImageData(data.Texture));
        }
Esempio n. 5
0
        /// <summary>
        /// Function to resize the image to a new width, height and/or depth.
        /// </summary>
        /// <param name="baseImage">The image to resize.</param>
        /// <param name="newWidth">The new width for the image.</param>
        /// <param name="newHeight">The new height for the image (for <see cref="ImageType.Image2D"/> and <see cref="ImageType.ImageCube"/> images).</param>
        /// <param name="newDepth">The new depth for the image (for <see cref="ImageType.Image3D"/> images).</param>
        /// <param name="filter">[Optional] The type of filtering to apply to the scaled image to help smooth larger and smaller images.</param>
        /// <returns>A <see cref="IGorgonImage"/> containing the resized image.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="baseImage"/> parameter is <b>null</b>.</exception>
        /// <exception cref="ArgumentOutOfRangeException">Thrown if the <paramref name="newWidth"/>, <paramref name="newHeight"/>, or <paramref name="newDepth"/> parameters are less than 1.</exception>
        /// <exception cref="GorgonException">Thrown if there was an error during resizing regarding image pixel format conversion due to the type of <paramref name="filter"/> applied.</exception>
        /// <remarks>
        /// <para>
        /// This method will change the size of an existing image and return a larger or smaller version of itself as a new <see cref="IGorgonImage"/>.
        /// </para>
        /// </remarks>
        public static IGorgonImage Resize(this IGorgonImage baseImage, int newWidth, int newHeight, int newDepth, ImageFilter filter = ImageFilter.Point)
        {
            if (baseImage == null)
            {
                throw new ArgumentNullException(nameof(baseImage));
            }

            if (newWidth < 1)
            {
                throw new ArgumentOutOfRangeException(Resources.GORIMG_ERR_IMAGE_WIDTH_TOO_SMALL, nameof(newWidth));
            }

            if ((newHeight < 1) && ((baseImage.ImageType == ImageType.Image2D) || (baseImage.ImageType == ImageType.ImageCube)))
            {
                throw new ArgumentOutOfRangeException(Resources.GORIMG_ERR_IMAGE_HEIGHT_TOO_SMALL, nameof(newHeight));
            }

            if ((newDepth < 1) && (baseImage.ImageType == ImageType.Image3D))
            {
                throw new ArgumentOutOfRangeException(Resources.GORIMG_ERR_IMAGE_DEPTH_TOO_SMALL, nameof(newDepth));
            }

            // Only use the appropriate dimensions.
            switch (baseImage.ImageType)
            {
            case ImageType.Image1D:
                newHeight = baseImage.Height;
                break;

            case ImageType.Image2D:
            case ImageType.ImageCube:
                newDepth = baseImage.Depth;
                break;
            }

            // If we haven't actually changed the size, then skip out.
            if ((newWidth == baseImage.Width) && (newHeight == baseImage.Height) && (newDepth == baseImage.Depth))
            {
                return(baseImage);
            }

            var wic = new WicUtilities();

            IGorgonImage newImage = null;

            try
            {
                int calcMipLevels = GorgonImage.CalculateMaxMipCount(newWidth, newHeight, newDepth).Min(baseImage.MipCount);
                newImage = wic.Resize(baseImage, 0, 0, newWidth, newHeight, newDepth, calcMipLevels, filter, ResizeMode.Scale);

                newImage.CopyTo(baseImage);

                return(baseImage);
            }
            finally
            {
                newImage?.Dispose();
                wic.Dispose();
            }
        }
Esempio n. 6
0
        /// <summary>
        /// Function to convert the image to B4G4R4A4.
        /// </summary>
        /// <param name="baseImage">The base image to convert.</param>
        /// <param name="dithering">Dithering to apply to the converstion to B8G8R8A8.</param>
        /// <returns>The updated image.</returns>
        private static IGorgonImage ConvertToB4G4R4A4(IGorgonImage baseImage, ImageDithering dithering)
        {
            // This temporary image will be used to convert to B8G8R8A8.
            IGorgonImage newImage = baseImage;

            IGorgonImageInfo destInfo = new GorgonImageInfo(baseImage.ImageType, BufferFormat.B4G4R4A4_UNorm)
            {
                Depth      = baseImage.Depth,
                Height     = baseImage.Height,
                Width      = baseImage.Width,
                ArrayCount = baseImage.ArrayCount,
                MipCount   = baseImage.MipCount
            };

            // This is our working buffer for B4G4R4A4.
            IGorgonImage destImage = new GorgonImage(destInfo);

            try
            {
                // If necessary, convert to B8G8R8A8. Otherwise, we'll just downsample directly.
                if ((newImage.Format != BufferFormat.B8G8R8A8_UNorm) &&
                    (newImage.Format != BufferFormat.R8G8B8A8_UNorm))
                {
                    newImage = baseImage.Clone();
                    ConvertToFormat(newImage, BufferFormat.B8G8R8A8_UNorm, dithering);
                }

                // The next step is to manually downsample to R4G4B4A4.
                // Because we're doing this manually, dithering won't be an option unless unless we've downsampled from a much higher bit format when converting to B8G8R8A8.
                for (int array = 0; array < newImage.ArrayCount; ++array)
                {
                    for (int mip = 0; mip < newImage.MipCount; ++mip)
                    {
                        int depthCount = newImage.GetDepthCount(mip);

                        for (int depth = 0; depth < depthCount; depth++)
                        {
                            IGorgonImageBuffer destBuffer = destImage.Buffers[mip, destInfo.ImageType == ImageType.Image3D ? depth : array];
                            IGorgonImageBuffer srcBuffer  = newImage.Buffers[mip, newImage.ImageType == ImageType.Image3D ? depth : array];

                            ConvertPixelsToB4G4R4A4(destBuffer, srcBuffer);
                        }
                    }
                }

                destImage.CopyTo(baseImage);

                return(baseImage);
            }
            finally
            {
                destImage.Dispose();

                if (newImage != baseImage)
                {
                    newImage.Dispose();
                }
            }
        }
Esempio n. 7
0
        /// <summary>
        /// Handles the Click event of the ButtonImagePath control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        private void ButtonImagePath_Click(object sender, EventArgs e)
        {
            Cursor.Current = Cursors.WaitCursor;

            IGorgonImage image = null;
            var          png   = new GorgonCodecPng();

            try
            {
                if (DialogOpenPng.ShowDialog(this) != DialogResult.OK)
                {
                    return;
                }

                TextImagePath.Text = DialogOpenPng.FileName;
                _sourceTexture?.Texture?.Dispose();
                _outputTexture?.Dispose();
                _sourceTexture = null;
                _outputTexture = null;

                image          = png.LoadFromFile(DialogOpenPng.FileName);
                _sourceTexture = image.ConvertToFormat(BufferFormat.R8G8B8A8_UNorm)
                                 .ToTexture2D(_graphics,
                                              new GorgonTexture2DLoadOptions
                {
                    Name =
                        Path.GetFileNameWithoutExtension(DialogOpenPng.FileName)
                }).GetShaderResourceView();

                _outputTexture = new GorgonTexture2D(_graphics,
                                                     new GorgonTexture2DInfo(_sourceTexture, "Output")
                {
                    Format  = BufferFormat.R8G8B8A8_Typeless,
                    Binding = TextureBinding.ShaderResource | TextureBinding.ReadWriteView
                });

                // Get an SRV for the output texture so we can render it later.
                _outputView = _outputTexture.GetShaderResourceView(BufferFormat.R8G8B8A8_UNorm);

                // Get a UAV for the output.
                _outputUav = _outputTexture.GetReadWriteView(BufferFormat.R32_UInt);

                // Process the newly loaded texture.
                _sobel.Process(_sourceTexture, _outputUav, TrackThickness.Value, TrackThreshold.Value / 100.0f);

                TrackThreshold.Enabled = TrackThickness.Enabled = true;
            }
            catch (Exception ex)
            {
                GorgonDialogs.ErrorBox(this, ex);
                TrackThreshold.Enabled = TrackThickness.Enabled = false;
            }
            finally
            {
                image?.Dispose();
                Cursor.Current = Cursors.Default;
            }
        }
Esempio n. 8
0
        /// <summary>
        /// Function to crop the image to the rectangle passed to the parameters.
        /// </summary>
        /// <param name="baseImage">The image to resize.</param>
        /// <param name="cropRect">The rectangle that will be used to crop the image.</param>
        /// <param name="newDepth">The new depth for the image (for <see cref="ImageType.Image3D"/> images).</param>
        /// <returns>A <see cref="IGorgonImage"/> containing the resized image.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="baseImage"/> parameter is <b>null</b>.</exception>
        /// <exception cref="ArgumentOutOfRangeException">Thrown if the <paramref name="newDepth"/> parameter is less than 1.</exception>
        /// <remarks>
        /// <para>
        /// This method will crop the existing image a smaller version of itself as a new <see cref="IGorgonImage"/>. If the sizes are the same, or the <paramref name="cropRect"/> is larger than the size
        /// of the <paramref name="baseImage"/>, then no changes will be made.
        /// </para>
        /// </remarks>
        public static IGorgonImage Crop(this IGorgonImage baseImage, DX.Rectangle cropRect, int newDepth)
        {
            if (baseImage == null)
            {
                throw new ArgumentNullException(nameof(baseImage));
            }

            if ((newDepth < 1) && (baseImage.ImageType == ImageType.Image3D))
            {
                throw new ArgumentOutOfRangeException(Resources.GORIMG_ERR_IMAGE_DEPTH_TOO_SMALL, nameof(newDepth));
            }

            // Only use the appropriate dimensions.
            switch (baseImage.ImageType)
            {
            case ImageType.Image1D:
                cropRect.Height = baseImage.Height;
                break;

            case ImageType.Image2D:
            case ImageType.ImageCube:
                newDepth = baseImage.Depth;
                break;
            }

            // If the intersection of the crop rectangle and the source buffer are the same (and the depth is the same), then we don't need to crop.
            var bufferRect = new DX.Rectangle(0, 0, baseImage.Width, baseImage.Height);
            var clipRect   = DX.Rectangle.Intersect(cropRect, bufferRect);

            if ((bufferRect.Equals(ref clipRect)) && (newDepth == baseImage.Depth))
            {
                return(baseImage);
            }

            var wic = new WicUtilities();

            IGorgonImage newImage = null;

            try
            {
                int calcMipLevels = GorgonImage.CalculateMaxMipCount(cropRect.Width, cropRect.Height, newDepth).Min(baseImage.MipCount);
                newImage = wic.Resize(baseImage, cropRect.X, cropRect.Y, cropRect.Width, cropRect.Height, newDepth, calcMipLevels, ImageFilter.Point, ResizeMode.Crop);

                // Send the data over to the new image.
                newImage.CopyTo(baseImage);

                return(baseImage);
            }
            finally
            {
                newImage?.Dispose();
                wic.Dispose();
            }
        }
Esempio n. 9
0
        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.Form.FormClosing" /> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.FormClosingEventArgs" /> that contains the event data.</param>
        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            base.OnFormClosing(e);

            GorgonExample.UnloadResources();

            _pluginCache?.Dispose();
            _texture?.Dispose();
            _swap?.Dispose();
            _graphics?.Dispose();
            _image?.Dispose();
        }
Esempio n. 10
0
        /// <summary>
        /// Function to convert the pixel format of an image into another pixel format.
        /// </summary>
        /// <param name="baseImage">The image to convert.</param>
        /// <param name="format">The new pixel format for the image.</param>
        /// <param name="dithering">[Optional] Flag to indicate the type of dithering to perform when the bit depth for the <paramref name="format"/> is lower than the original bit depth.</param>
        /// <returns>A <see cref="IGorgonImage"/> containing the image data with the converted pixel format.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="baseImage"/> is <b>null</b>.</exception>
        /// <exception cref="ArgumentEmptyException">Thrown when the <paramref name="format"/> is set to <see cref="BufferFormat.Unknown"/>.
        /// <para>-or-</para>
        /// <para>Thrown when the original format could not be converted into the desired <paramref name="format"/>.</para>
        /// </exception>
        /// <remarks>
        /// <para>
        /// Use this to convert an image format from one to another. The conversion functionality uses Windows Imaging Components (WIC) to perform the conversion.
        /// </para>
        /// <para>
        /// Because this method uses WIC, not all formats will be convertible. To determine if a format can be converted, use the <see cref="GorgonImage.CanConvertToFormat"/> method.
        /// </para>
        /// <para>
        /// For the <see cref="BufferFormat.B4G4R4A4_UNorm"/> format, Gorgon has to perform a manual conversion since that format is not supported by WIC. Because of this, the
        /// <paramref name="dithering"/> flag will be ignored when downsampling to that format.
        /// </para>
        /// </remarks>
        public static IGorgonImage ConvertToFormat(this IGorgonImage baseImage, BufferFormat format, ImageDithering dithering = ImageDithering.None)
        {
            if (baseImage == null)
            {
                throw new ArgumentNullException(nameof(baseImage));
            }

            if ((format == BufferFormat.Unknown) || (!baseImage.CanConvertToFormat(format)))
            {
                throw new ArgumentException(string.Format(Resources.GORIMG_ERR_FORMAT_NOT_SUPPORTED, format), nameof(format));
            }

            if (format == baseImage.Format)
            {
                return(baseImage);
            }

            // If we've asked for 4 bit per channel BGRA, then we have to convert the base image to B8R8G8A8,and then convert manually (no support in WIC).
            if (format == BufferFormat.B4G4R4A4_UNorm)
            {
                return(ConvertToB4G4R4A4(baseImage, dithering));
            }

            // If we're currently using B4G4R4A4, then manually convert (no support in WIC).
            if (baseImage.Format == BufferFormat.B4G4R4A4_UNorm)
            {
                return(ConvertFromB4G4R4A4(baseImage, format));
            }

            var          destInfo = new GorgonFormatInfo(format);
            WicUtilities wic      = null;

            IGorgonImage newImage = null;

            try
            {
                wic      = new WicUtilities();
                newImage = wic.ConvertToFormat(baseImage, format, dithering, baseImage.FormatInfo.IsSRgb, destInfo.IsSRgb);

                newImage.CopyTo(baseImage);

                return(baseImage);
            }
            finally
            {
                newImage?.Dispose();
                wic?.Dispose();
            }
        }
Esempio n. 11
0
        /// <summary>
        /// Function to convert the image to use our custom codec.
        /// </summary>
        private void ConvertImage()
        {
            // The path to our image file for our custom codec.
            string tempPath = Path.ChangeExtension(Path.GetTempPath().FormatDirectory(Path.DirectorySeparatorChar) + Path.GetRandomFileName(), "tvImage");

            try
            {
                // Save the current texture using our useless new custom codec.
                _customCodec.SaveToFile(_image.ConvertToFormat(BufferFormat.R8G8B8A8_UNorm), tempPath);
                _image.Dispose();
                _texture?.Dispose();

                _image = _customCodec.LoadFromFile(tempPath);

                _texture = _image.ToTexture2D(_graphics, new GorgonTexture2DLoadOptions
                {
                    Name = "Converted Texture"
                }).GetShaderResourceView();
            }
            catch
            {
                // Clean up the new texture should we have an exception (this shouldn't happen, better safe than sorry).
                _image?.Dispose();
                throw;
            }
            finally
            {
                try
                {
                    File.Delete(tempPath);
                }
                // ReSharper disable once EmptyGeneralCatchClause
                catch
                {
                    // Intentionally left blank.
                    // If we can't clean up the temp file, then it's no big deal right now.
                }
            }
        }
Esempio n. 12
0
        /// <summary>Function to provide clean up for the plugin.</summary>
        protected override void OnShutdown()
        {
            try
            {
                if (_settings != null)
                {
                    // Persist any settings.
                    ContentPlugInService.WriteContentSettings(typeof(SpriteEditorPlugIn).FullName, _settings, new JsonSharpDxRectConverter());
                }
            }
            catch (Exception ex)
            {
                // We don't care if it crashes. The worst thing that'll happen is your settings won't persist.
                CommonServices.Log.LogException(ex);
            }

            _rtv?.Dispose();
            _bgPattern?.Dispose();
            _noImage?.Dispose();

            ViewFactory.Unregister <ISpriteContent>();

            base.OnShutdown();
        }
Esempio n. 13
0
        /// <summary>Function to retrieve a thumbnail for the content.</summary>
        /// <param name="contentFile">The content file used to retrieve the data to build the thumbnail with.</param>
        /// <param name="fileManager">The content file manager.</param>
        /// <param name="outputFile">The output file for the thumbnail data.</param>
        /// <param name="cancelToken">The token used to cancel the thumbnail generation.</param>
        /// <returns>A <see cref="T:Gorgon.Graphics.Imaging.IGorgonImage"/> containing the thumbnail image data.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="contentFile" />, <paramref name="fileManager" /> or the <paramref name="outputFile" /> parameter is <b>null</b>.</exception>
        public async Task <IGorgonImage> GetThumbnailAsync(IContentFile contentFile, IContentFileManager fileManager, FileInfo outputFile, CancellationToken cancelToken)
        {
            if (contentFile == null)
            {
                throw new ArgumentNullException(nameof(contentFile));
            }

            if (fileManager == null)
            {
                throw new ArgumentNullException(nameof(fileManager));
            }

            if (outputFile == null)
            {
                throw new ArgumentNullException(nameof(outputFile));
            }

            // If the content is not a v3 sprite, then leave it.
            if ((!contentFile.Metadata.Attributes.TryGetValue(SpriteContent.CodecAttr, out string codecName)) ||
                (string.IsNullOrWhiteSpace(codecName)) ||
                (!string.Equals(codecName, _defaultCodec.GetType().FullName, StringComparison.OrdinalIgnoreCase)))
            {
                return(null);
            }

            if (!outputFile.Directory.Exists)
            {
                outputFile.Directory.Create();
                outputFile.Directory.Refresh();
            }

            IGorgonImageCodec pngCodec = new GorgonCodecPng();

            (IGorgonImage image, IContentFile imageFile, GorgonSprite sprite) = await Task.Run(() => LoadThumbnailImage(pngCodec, outputFile, contentFile, fileManager, cancelToken));

            if ((image == null) || (cancelToken.IsCancellationRequested))
            {
                return(null);
            }

            // We loaded a cached thumbnail.
            if ((sprite == null) || (imageFile == null))
            {
                return(image);
            }

            // We need to switch back to the main thread here to render the image, otherwise things will break.
            Cursor.Current = Cursors.WaitCursor;

            GorgonTexture2DView spriteTexture = null;
            IGorgonImage        resultImage   = null;

            try
            {
                spriteTexture = GorgonTexture2DView.CreateTexture(GraphicsContext.Graphics, new GorgonTexture2DInfo(imageFile.Path)
                {
                    Width   = image.Width,
                    Height  = image.Height,
                    Format  = image.Format,
                    Binding = TextureBinding.ShaderResource,
                    Usage   = ResourceUsage.Default
                }, image);
                sprite.Texture = spriteTexture;

                resultImage = RenderThumbnail(sprite);

                await Task.Run(() => pngCodec.SaveToFile(resultImage, outputFile.FullName), cancelToken);

                if (cancelToken.IsCancellationRequested)
                {
                    return(null);
                }

                contentFile.Metadata.Attributes[CommonEditorConstants.ThumbnailAttr] = outputFile.Name;
                return(resultImage);
            }
            catch (Exception ex)
            {
                CommonServices.Log.Print($"[ERROR] Cannot create thumbnail for '{contentFile.Path}'", LoggingLevel.Intermediate);
                CommonServices.Log.LogException(ex);
                return(null);
            }
            finally
            {
                image?.Dispose();
                spriteTexture?.Dispose();
                Cursor.Current = Cursors.Default;
            }
        }
Esempio n. 14
0
        /// <summary>
        /// Function to show the tool form.
        /// </summary>
        private void ShowForm()
        {
            FormExtract         _form   = null;
            GorgonTexture2DView texture = null;
            Stream fileStream           = null;
            ExtractSpriteToolSettings settings;
            IGorgonImage image = null;
            IContentFile textureFile;

            CommonServices.BusyService.SetBusy();

            try
            {
                settings = ToolPlugInService.ReadContentSettings <ExtractSpriteToolSettings>(typeof(ExtractSpriteToolPlugIn).FullName);

                if (settings == null)
                {
                    settings = new ExtractSpriteToolSettings();
                }

                textureFile = _fileManager.SelectedFile;
                fileStream  = textureFile.OpenRead();

                texture = GorgonTexture2DView.FromStream(GraphicsContext.Graphics, fileStream, _defaultImageCodec, options: new GorgonTexture2DLoadOptions
                {
                    Name          = "Extractor Texture Atlas",
                    Binding       = TextureBinding.ShaderResource,
                    Usage         = ResourceUsage.Default,
                    IsTextureCube = false
                });

                image = texture.Texture.ToImage();

                fileStream.Dispose();

                if (string.IsNullOrWhiteSpace(settings.LastOutputDir))
                {
                    settings.LastOutputDir = Path.GetDirectoryName(textureFile.Path).FormatDirectory('/');
                }
                else
                {
                    if (!_fileManager.DirectoryExists(settings.LastOutputDir.FormatDirectory('/')))
                    {
                        settings.LastOutputDir = Path.GetDirectoryName(textureFile.Path).FormatDirectory('/');
                    }
                }

                _extractData.Value.Texture   = texture;
                _extractData.Value.SkipEmpty = settings.AllowEmptySpriteSkip;
                _extractData.Value.SkipColor = new GorgonColor(settings.SkipColor);

                var extractViewModel = new Extract();
                extractViewModel.Initialize(new ExtractParameters(settings,
                                                                  _extractData.Value,
                                                                  textureFile,
                                                                  new ExtractorService(GraphicsContext.Renderer2D, _fileManager,
                                                                                       new GorgonV3SpriteBinaryCodec(GraphicsContext.Renderer2D)),
                                                                  new ColorPickerService(),
                                                                  FolderBrowser,
                                                                  CommonServices));

                _form = new FormExtract();
                _form.SetupGraphics(GraphicsContext);
                _form.SetDataContext(extractViewModel);
                _form.ShowDialog(GorgonApplication.MainForm);

                ToolPlugInService.WriteContentSettings(typeof(ExtractSpriteToolPlugIn).FullName, settings);
            }
            catch (Exception ex)
            {
                CommonServices.MessageDisplay.ShowError(ex, Resources.GOREST_ERR_LAUNCH);
            }
            finally
            {
                _form?.Dispose();
                fileStream?.Dispose();
                image?.Dispose();
                texture?.Dispose();
                CommonServices.BusyService.SetIdle();
            }
        }
Esempio n. 15
0
        /// <summary>Function to save the atlas data.</summary>
        /// <param name="atlas">The atlas data to save.</param>
        public void SaveAtlas(IReadOnlyDictionary <IContentFile, GorgonSprite> spriteFiles, GorgonTextureAtlas atlas)
        {
            IGorgonImage image     = null;
            string       directory = Path.GetDirectoryName(atlas.Textures[0].Texture.Name).FormatDirectory('/');
            Stream       outStream = null;

            try
            {
                _fileSystem.BeginBatch();

                if (!_fileSystem.DirectoryExists(directory))
                {
                    _fileSystem.CreateDirectory(directory);
                }

                void WriteImageFile(Stream stream) => _defaultImageCodec.SaveToStream(image, stream);

                // Check the files to ensure they're not open for editing.
                foreach (GorgonTexture2DView texture in atlas.Textures)
                {
                    IContentFile textureFile = _fileSystem.GetFile(texture.Texture.Name);

                    if ((textureFile != null) && (textureFile.IsOpen))
                    {
                        throw new IOException(string.Format(Resources.GORTAG_ERR_IMAGE_OPEN, textureFile.Path));
                    }
                }

                foreach ((GorgonSprite original, GorgonSprite sprite) in atlas.Sprites)
                {
                    IContentFile oldFile = spriteFiles.FirstOrDefault(item => item.Value == original).Key;

                    if ((oldFile != null) && (oldFile.IsOpen))
                    {
                        throw new IOException(string.Format(Resources.GORTAG_ERR_IMAGE_OPEN, oldFile.Path));
                    }
                }

                // Write textures.
                foreach (GorgonTexture2DView texture in atlas.Textures)
                {
                    image = texture.Texture.ToImage();
                    _fileSystem.WriteFile(texture.Texture.Name, WriteImageFile);
                    image.Dispose();
                }

                // Write out the updated sprites.
                foreach ((GorgonSprite original, GorgonSprite sprite) in atlas.Sprites)
                {
                    IContentFile oldTextureFile = null;
                    IContentFile textureFile    = _fileSystem.GetFile(sprite.Texture.Texture.Name);
                    IContentFile oldFile        = spriteFiles.FirstOrDefault(item => item.Value == original).Key;

                    if (oldFile.Metadata.DependsOn.TryGetValue(CommonEditorContentTypes.ImageType, out string oldTexturePath))
                    {
                        oldTextureFile = _fileSystem.GetFile(oldTexturePath);
                    }

                    outStream = oldFile.OpenWrite();
                    _defaultSpriteCodec.Save(sprite, outStream);

                    textureFile.LinkContent(oldFile);
                    oldTextureFile?.UnlinkContent(oldFile);

                    oldFile.RefreshMetadata();
                    oldFile.Refresh();
                    outStream.Dispose();
                }
            }
            finally
            {
                outStream?.Dispose();
                image?.Dispose();
                _fileSystem.EndBatch();
            }
        }
Esempio n. 16
0
        /// <summary>
        /// Function to create a compatible image to use when drawing with this effect.
        /// </summary>
        /// <param name="diffuse">The diffuse map.</param>
        /// <param name="normalMap">The normal map.</param>
        /// <param name="specularMap">[Optional] The specular map.</param>
        /// <returns>A new image with the maps combined as an image array to use with the effect.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="diffuse"/>, or the <paramref name="normalMap"/> parameter is <b>null</b>.</exception>
        /// <exception cref="ArgumentException">Thrown if any of the images passed in are not a <see cref="ImageType.Image2D"/> type.</exception>
        /// <remarks>
        /// <para>
        /// Since the lighting effect requires that textures be drawn using an array of 3 images, developers can use this helper method to create a new image based on separate images that will be suitable
        /// for rendering with this effect.
        /// </para>
        /// <para>
        /// The resulting image from this method is guaranteed to contain the correct image data, in the correct order, for use with this effect. The image can then be used in any of the
        /// texture types for rendering.
        /// </para>
        /// <para>
        /// The images passed to this method must be a 2D image type, otherwise an exception will thrown.
        /// </para>
        /// <para>
        /// All images passed to this method should be the same width, height and pixel format. Otherwise undesired artifacts may appear on the generated maps.
        /// </para>
        /// </remarks>
        /// <seealso cref="IGorgonImage"/>
        /// <seealso cref="GorgonTexture2D"/>
        /// <seealso cref="GorgonTexture2DView"/>
        public IGorgonImage CreateLightingImage(IGorgonImage diffuse, IGorgonImage normalMap, IGorgonImage specularMap = null)
        {
            if (diffuse == null)
            {
                throw new ArgumentNullException(nameof(diffuse));
            }

            if (normalMap == null)
            {
                throw new ArgumentNullException(nameof(normalMap));
            }

            if (diffuse.ImageType != ImageType.Image2D)
            {
                throw new ArgumentException(string.Format(Resources.GOR2D_ERR_2D_IMAGE_ONLY, nameof(diffuse)));
            }

            if (normalMap.ImageType != ImageType.Image2D)
            {
                throw new ArgumentException(string.Format(Resources.GOR2D_ERR_2D_IMAGE_ONLY, nameof(normalMap)));
            }

            if ((specularMap != null) && (specularMap.ImageType != ImageType.Image2D))
            {
                throw new ArgumentException(string.Format(Resources.GOR2D_ERR_2D_IMAGE_ONLY, nameof(specularMap)));
            }

            IGorgonImage result       = null;
            IGorgonImage workSpecular = specularMap?.Clone();
            IGorgonImage workDiffuse  = diffuse.Clone();
            IGorgonImage workNormal   = normalMap.Clone();

            if (workSpecular == null)
            {
                workSpecular = new GorgonImage(new GorgonImageInfo(ImageType.Image2D, diffuse.Format)
                {
                    Width  = diffuse.Width,
                    Height = diffuse.Height
                });
                workSpecular.Buffers[0].Fill(0x00);
            }

            try
            {
                // Ensure formats are the same across all array entries.
                if (workNormal.Format != workDiffuse.Format)
                {
                    workNormal.ConvertToFormat(workDiffuse.Format);
                }

                if (workSpecular.Format != workDiffuse.Format)
                {
                    workSpecular.ConvertToFormat(workDiffuse.Format);
                }

                // Ensure width and height matches.
                if ((workNormal.Width != workDiffuse.Width) || (workDiffuse.Height != workNormal.Height))
                {
                    workNormal.Resize(workDiffuse.Width, workDiffuse.Height, 1);
                }

                if ((workSpecular.Width != workDiffuse.Width) || (workSpecular.Height != workNormal.Height))
                {
                    workSpecular.Resize(workDiffuse.Width, workDiffuse.Height, 1);
                }

                var info = new GorgonImageInfo(ImageType.Image2D, diffuse.Format)
                {
                    Width      = diffuse.Width,
                    Height     = diffuse.Height,
                    ArrayCount = 3
                };

                result = new GorgonImage(info);
                workDiffuse.Buffers[0].CopyTo(result.Buffers[0, 0]);
                workSpecular.Buffers[0].CopyTo(result.Buffers[0, 1]);
                workNormal.Buffers[0].CopyTo(result.Buffers[0, 2]);

                return(result);
            }
            catch
            {
                result?.Dispose();
                throw;
            }
            finally
            {
                workDiffuse?.Dispose();
                workNormal?.Dispose();
                workSpecular.Dispose();
            }
        }