Ejemplo n.º 1
0
        /// <summary>
        /// Function to load the image to be used a thumbnail.
        /// </summary>
        /// <param name="thumbnailCodec">The codec for the thumbnail images.</param>
        /// <param name="thumbnailFile">The path to the thumbnail file.</param>
        /// <param name="content">The content being thumbnailed.</param>
        /// <param name="fileManager">The file manager used to handle content files.</param>
        /// <param name="cancelToken">The token used to cancel the operation.</param>
        /// <returns>The image, image content file and sprite, or just the thumbnail image if it was cached (sprite will be null).</returns>
        private (IGorgonImage image, IContentFile imageFile, GorgonSprite sprite) LoadThumbnailImage(IGorgonImageCodec thumbnailCodec, FileInfo thumbnailFile, IContentFile content, IContentFileManager fileManager, CancellationToken cancelToken)
        {
            IGorgonImage spriteImage;
            Stream       inStream  = null;
            Stream       imgStream = null;

            try
            {
                // If we've already got the file, then leave.
                if (thumbnailFile.Exists)
                {
                    inStream    = thumbnailFile.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
                    spriteImage = thumbnailCodec.LoadFromStream(inStream);

#pragma warning disable IDE0046 // Convert to conditional expression
                    if (cancelToken.IsCancellationRequested)
                    {
                        return(null, null, null);
                    }
#pragma warning restore IDE0046 // Convert to conditional expression

                    return(spriteImage, null, null);
                }

                IContentFile imageFile = FindImage(content, fileManager);
                if (imageFile == null)
                {
                    return(_noImage.Clone(), null, null);
                }

                imgStream = imageFile.OpenRead();
                inStream  = content.OpenRead();

                if ((!_ddsCodec.IsReadable(imgStream)) ||
                    (!_defaultCodec.IsReadable(inStream)))
                {
                    return(_noImage.Clone(), null, null);
                }

                spriteImage = _ddsCodec.LoadFromStream(imgStream);
                GorgonSprite sprite = _defaultCodec.FromStream(inStream);

                return(spriteImage, imageFile, sprite);
            }
            catch (Exception ex)
            {
                CommonServices.Log.Print($"[ERROR] Cannot create thumbnail for '{content.Path}'", LoggingLevel.Intermediate);
                CommonServices.Log.LogException(ex);
                return(null, null, null);
            }
            finally
            {
                imgStream?.Dispose();
                inStream?.Dispose();
            }
        }
Ejemplo n.º 2
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();
                }
            }
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Function to set the alpha value for an image.
        /// </summary>
        /// <param name="sourceImage">The source image.</param>
        /// <param name="currentMipLevel">The current mip map level.</param>
        /// <param name="currentArrayOrDepth">The current array index or depth slice.</param>
        /// <param name="value">The value to assign.</param>
        /// <param name="inclusionRange">The range of alpha values to update.</param>
        /// <returns>A new image with the updated alpha.</returns>
        public IGorgonImage SetAlphaValue(IGorgonImage sourceImage, int currentMipLevel, int currentArrayOrDepth, int value, GorgonRange inclusionRange)
        {
            IGorgonImage result = sourceImage.Clone();

            result.Buffers[currentMipLevel, currentArrayOrDepth]
            .SetAlpha(value / 255.0f, new GorgonRangeF(inclusionRange.Minimum / 255.0f, inclusionRange.Maximum / 255.0f));

            return(result);
        }
Ejemplo n.º 4
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();
            }
        }