/// <summary>
        /// Function to determine if this codec can read the image data within the stream or not.
        /// </summary>
        /// <param name="stream">The stream that is used to read the image data.</param>
        /// <returns><b>true</b> if the codec can read the file, <b>false</b> if not.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="stream"/> parameter is <b>null</b>.</exception>
        /// <exception cref="IOException">Thrown when the <paramref name="stream"/> is write-only or if the stream cannot perform seek operations.</exception>
        /// <remarks>
        /// <para>
        /// When overloading this method, the implementor should remember to reset the stream position back to the original position when they are done reading the data.  Failure to do so may cause
        /// undesirable results or an exception.
        /// </para>
        /// </remarks>
        public override bool IsReadable(Stream stream)
        {
            if (stream == null)
            {
                throw new ArgumentNullException(nameof(stream));
            }

            if (!stream.CanRead)
            {
                throw new IOException(Resources.GORIMG_ERR_STREAM_IS_WRITEONLY);
            }

            if (!stream.CanSeek)
            {
                throw new IOException(Resources.GORIMG_ERR_STREAM_CANNOT_SEEK);
            }

            var wic = new WicUtilities();

            try
            {
                GorgonImageInfo info = wic.GetImageMetaDataFromStream(stream, SupportedFileFormat, null);

                return(info == null ? false : info.Format != BufferFormat.Unknown);
            }
            catch (DX.SharpDXException)
            {
                return(false);
            }
            finally
            {
                wic.Dispose();
            }
        }
Exemple #2
0
        /// <summary>
        /// Function to convert a <see cref="Bitmap"/> into a <see cref="IGorgonImage"/>.
        /// </summary>
        /// <param name="bitmap">The <see cref="Bitmap"/> to convert.</param>
        /// <returns>A new <see cref="IGorgonImage"/> containing the data from the <see cref="Bitmap"/>.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="bitmap"/> parameter is <b>null</b>.</exception>
        /// <exception cref="GorgonException">Thrown if the <paramref name="bitmap"/> is not <see cref="PixelFormat.Format32bppArgb"/>.</exception>
        /// <remarks>
        /// <para>
        /// This method will take a 2D <see cref="Bitmap"/> and copy its data into a new 2D <see cref="IGorgonImage"/>. The resulting <see cref="IGorgonImage"/> will only contain 1 array level,
        /// and no mip map levels.
        /// </para>
        /// <para>
        /// Some format conversion is performed on the <paramref name="bitmap"/> when it is imported. The format conversion will always convert to the image format of
        /// <see cref="BufferFormat.R8G8B8A8_UNorm"/>. Only the following GDI+ pixel formats are supported for conversion:
        /// <list type="bullet">
        ///     <item>
        ///         <term><see cref="PixelFormat.Format32bppArgb"/></term>
        ///     </item>
        ///     <item>
        ///         <term><see cref="PixelFormat.Format32bppPArgb"/></term>
        ///     </item>
        ///     <item>
        ///         <term><see cref="PixelFormat.Format32bppRgb"/></term>
        ///     </item>
        ///     <item>
        ///         <term><see cref="PixelFormat.Format24bppRgb"/></term>
        ///     </item>
        /// </list>
        /// If the source <paramref name="bitmap"/> does not support any of the formats on the list, then an exception will be thrown.
        /// </para>
        /// </remarks>
        public static IGorgonImage ToGorgonImage(this Bitmap bitmap)
        {
            if (bitmap == null)
            {
                throw new ArgumentNullException(nameof(bitmap));
            }

            if ((bitmap.PixelFormat != PixelFormat.Format32bppArgb) &&
                (bitmap.PixelFormat != PixelFormat.Format32bppPArgb) &&
                (bitmap.PixelFormat != PixelFormat.Format32bppRgb) &&
                (bitmap.PixelFormat != PixelFormat.Format24bppRgb))
            {
                throw new GorgonException(GorgonResult.FormatNotSupported, string.Format(Resources.GORIMG_ERR_FORMAT_NOT_SUPPORTED, bitmap.PixelFormat));
            }

            IGorgonImageInfo info = new GorgonImageInfo(ImageType.Image2D, BufferFormat.R8G8B8A8_UNorm)
            {
                Width  = bitmap.Width,
                Height = bitmap.Height
            };

            IGorgonImage result     = new GorgonImage(info);
            BitmapData   bitmapLock = null;

            try
            {
                bitmapLock = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);

                if ((bitmap.PixelFormat == PixelFormat.Format32bppArgb) ||
                    (bitmap.PixelFormat == PixelFormat.Format32bppPArgb) ||
                    (bitmap.PixelFormat == PixelFormat.Format32bppRgb))
                {
                    Transfer32Argb(bitmapLock, result.Buffers[0]);
                }
                else
                {
                    Transfer24Rgb(bitmapLock, result.Buffers[0]);
                }
            }
            catch
            {
                result.Dispose();
                throw;
            }
            finally
            {
                if (bitmapLock != null)
                {
                    bitmap.UnlockBits(bitmapLock);
                }
            }

            return(result);
        }
        /// <summary>
        /// Function to read file meta data.
        /// </summary>
        /// <param name="stream">Stream used to read the meta data.</param>
        /// <param name="options">Options used for decoding the meta data.</param>
        /// <returns>
        /// The image meta data as a <see cref="GorgonImageInfo"/> value.
        /// </returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="stream"/> parameter is <b>null</b>.</exception>
        /// <exception cref="IOException">Thrown when the stream is write-only.
        /// <para>-or-</para>
        /// <para>Thrown when the stream cannot perform seek operations.</para>
        /// </exception>
        /// <exception cref="EndOfStreamException">Thrown when an attempt to read beyond the end of the stream is made.</exception>
        public IGorgonImageInfo GetMetaData(Stream stream, IGorgonWicDecodingOptions options)
        {
            if (stream == null)
            {
                throw new ArgumentNullException(nameof(stream));
            }

            if (!stream.CanRead)
            {
                throw new IOException(Resources.GORIMG_ERR_STREAM_IS_WRITEONLY);
            }

            if (!stream.CanSeek)
            {
                throw new IOException(Resources.GORIMG_ERR_STREAM_CANNOT_SEEK);
            }

            var wic = new WicUtilities();

            try
            {
                // Get our WIC interface.
                GorgonImageInfo result = wic.GetImageMetaDataFromStream(stream, SupportedFileFormat, options);

                if (result.Format == BufferFormat.Unknown)
                {
                    throw new IOException(string.Format(Resources.GORIMG_ERR_FORMAT_NOT_SUPPORTED, result.Format));
                }

                return(result);
            }
            catch (DX.SharpDXException)
            {
                throw new IOException(string.Format(Resources.GORIMG_ERR_FILE_FORMAT_NOT_CORRECT, Codec));
            }
            finally
            {
                wic.Dispose();
            }
        }
Exemple #4
0
        /// <summary>
        /// Function to read the meta data for the image.
        /// </summary>
        /// <param name="stream">Stream containing the image data.</param>
        /// <returns>An image settings object containing information about the image.</returns>
        private static GorgonImageInfo ReadMetaData(Stream stream)
        {
            if (!stream.CanRead)
            {
                throw new ArgumentException(@"Stream is write only.", nameof(stream));
            }

            if (stream.Position + TvHeader.SizeInBytes >= stream.Length)
            {
                throw new EndOfStreamException();
            }

            // We only support 2D images with the tv format.
            var      settings = new GorgonImageInfo(ImageType.Image2D, BufferFormat.R8G8B8A8_UNorm);
            TvHeader header;

            // Load the header for the image.
            using (var reader = new GorgonBinaryReader(stream, true))
            {
                header = reader.ReadValue <TvHeader>();
            }

            // Ensure we've got the correct data.
            if (header.MagicValueData != MagicValue)
            {
                throw new ArgumentException(@"The image data is not a tv image.", nameof(stream));
            }

            // Ensure the width/height are valid.
            if ((header.Width < 0) ||
                (header.Height < 0))
            {
                throw new ArgumentException(@"The image in this stream has an invalid width/height.", nameof(stream));
            }

            settings.Width  = header.Width;
            settings.Height = header.Height;

            return(settings);
        }
Exemple #5
0
        /// <summary>
        /// Function to convert the specified image into a 2D image.
        /// </summary>
        /// <param name="image">The image to convert.</param>
        /// <param name="isCubeMap"><b>true</b> if the image should be a cube map, or <b>false</b> if not.</param>
        /// <returns>The converted image.</returns>
        public IGorgonImage ConvertTo2D(IGorgonImage image, bool isCubeMap)
        {
            IGorgonImage result = null;

            try
            {
                var info = new GorgonImageInfo(image, isCubeMap ? ImageType.ImageCube : ImageType.Image2D)
                {
                    ArrayCount = image.ArrayCount,
                    Depth      = 1
                };

                if (isCubeMap)
                {
                    while ((info.ArrayCount % 6) != 0)
                    {
                        ++info.ArrayCount;
                    }
                }

                result = new GorgonImage(info);

                // Copy the mip levels and array indices (for cube -> 2D and 2D -> cube, 3D always has an array count of 1).
                for (int array = 0; array < image.ArrayCount; ++array)
                {
                    for (int mip = 0; mip < result.MipCount; ++mip)
                    {
                        image.Buffers[mip, array].CopyTo(result.Buffers[mip, array]);
                    }
                }
            }
            catch
            {
                result?.Dispose();
                throw;
            }

            return(result);
        }
Exemple #6
0
        /// <summary>
        /// Function to read in the TGA header from a stream.
        /// </summary>
        /// <param name="reader">The reader used to read the stream containing the data.</param>
        /// <param name="conversionFlags">Flags for conversion.</param>
        /// <returns>New image settings.</returns>
        private static IGorgonImageInfo ReadHeader(GorgonBinaryReader reader, out TGAConversionFlags conversionFlags)
        {
            conversionFlags = TGAConversionFlags.None;

            // Get the header for the file.
            TgaHeader header = reader.ReadValue <TgaHeader>();

            if ((header.ColorMapType != 0) || (header.ColorMapLength != 0) ||
                (header.Width <= 0) || (header.Height <= 0) ||
                ((header.Descriptor & TgaDescriptor.Interleaved2Way) == TgaDescriptor.Interleaved2Way) ||
                ((header.Descriptor & TgaDescriptor.Interleaved4Way) == TgaDescriptor.Interleaved4Way))
            {
                throw new NotSupportedException(Resources.GORIMG_ERR_TGA_TYPE_NOT_SUPPORTED);
            }

            BufferFormat pixelFormat = BufferFormat.Unknown;

            switch (header.ImageType)
            {
            case TgaImageType.TrueColor:
            case TgaImageType.TrueColorRLE:
                switch (header.BPP)
                {
                case 16:
                    pixelFormat = BufferFormat.B5G5R5A1_UNorm;
                    break;

                case 24:
                case 32:
                    pixelFormat = BufferFormat.R8G8B8A8_UNorm;
                    if (header.BPP == 24)
                    {
                        conversionFlags |= TGAConversionFlags.Expand;
                    }
                    break;
                }

                if (header.ImageType == TgaImageType.TrueColorRLE)
                {
                    conversionFlags |= TGAConversionFlags.RLE;
                }
                break;

            case TgaImageType.BlackAndWhite:
            case TgaImageType.BlackAndWhiteRLE:
                if (header.BPP == 8)
                {
                    pixelFormat = BufferFormat.R8_UNorm;
                }
                else
                {
                    throw new IOException(string.Format(Resources.GORIMG_ERR_FORMAT_NOT_SUPPORTED, header.ImageType));
                }

                if (header.ImageType == TgaImageType.BlackAndWhiteRLE)
                {
                    conversionFlags |= TGAConversionFlags.RLE;
                }
                break;

            default:
                throw new IOException(string.Format(Resources.GORIMG_ERR_FORMAT_NOT_SUPPORTED, header.ImageType));
            }

            var settings = new GorgonImageInfo(ImageType.Image2D, pixelFormat)
            {
                MipCount   = 1,
                ArrayCount = 1,
                Width      = header.Width,
                Height     = header.Height
            };

            if ((header.Descriptor & TgaDescriptor.InvertX) == TgaDescriptor.InvertX)
            {
                conversionFlags |= TGAConversionFlags.InvertX;
            }

            if ((header.Descriptor & TgaDescriptor.InvertY) == TgaDescriptor.InvertY)
            {
                conversionFlags |= TGAConversionFlags.InvertY;
            }

            if (header.IDLength <= 0)
            {
                return(settings);
            }

            // Skip these bytes.
            for (int i = 0; i < header.IDLength; i++)
            {
                reader.ReadByte();
            }

            return(settings);
        }
Exemple #7
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();
            }
        }
Exemple #8
0
        private void GenerateTextures(Dictionary <Bitmap, IEnumerable <GlyphInfo> > glyphData)
        {
            var imageSettings = new GorgonImageInfo(ImageType.Image2D, BufferFormat.R8G8B8A8_UNorm)
            {
                Width  = Info.TextureWidth,
                Height = Info.TextureHeight,
                Depth  = 1
            };
            var textureSettings = new GorgonTexture2DInfo
            {
                Format          = BufferFormat.R8G8B8A8_UNorm,
                Width           = Info.TextureWidth,
                Height          = Info.TextureHeight,
                Usage           = ResourceUsage.Default,
                Binding         = TextureBinding.ShaderResource,
                IsCubeMap       = false,
                MipLevels       = 1,
                MultisampleInfo = GorgonMultisampleInfo.NoMultiSampling
            };

            GorgonImage     image       = null;
            GorgonTexture2D texture     = null;
            int             arrayIndex  = 0;
            int             bitmapCount = glyphData.Count;

            try
            {
                // We copy each bitmap into a texture array index until we've hit the max texture array size, and then
                // we move to a new texture.  This will keep our glyph textures inside of a single texture object until
                // it is absolutely necessary to change and should improve performance when rendering.
                foreach (KeyValuePair <Bitmap, IEnumerable <GlyphInfo> > glyphBitmap in glyphData)
                {
                    if ((image == null) || (arrayIndex >= Graphics.VideoAdapter.MaxTextureArrayCount))
                    {
                        textureSettings.ArrayCount = imageSettings.ArrayCount = bitmapCount.Min(Graphics.VideoAdapter.MaxTextureArrayCount);
                        arrayIndex = 0;

                        image?.Dispose();
                        image = new GorgonImage(imageSettings);

                        texture = image.ToTexture2D(Graphics, new GorgonTexture2DLoadOptions
                        {
                            Name = $"GorgonFont_{Name}_Internal_Texture_{Guid.NewGuid():N}"
                        });
                        _internalTextures.Add(texture);
                    }

                    CopyBitmap(glyphBitmap.Key, image, arrayIndex);

                    // Send to our texture.
                    texture.SetData(image.Buffers[0, arrayIndex], destArrayIndex: arrayIndex);

                    foreach (GlyphInfo info in glyphBitmap.Value)
                    {
                        info.Texture           = texture;
                        info.TextureArrayIndex = arrayIndex;
                    }

                    bitmapCount--;
                    arrayIndex++;
                }
            }
            finally
            {
                image?.Dispose();
            }
        }