Exemplo n.º 1
0
        /// <summary>
        /// Function to load a sprite from the editor file system.
        /// </summary>
        /// <param name="fileSystem">The file system containing the editor data.</param>
        /// <param name="renderer">The current renderer.</param>
        /// <param name="path">The path to the sprite.</param>
        /// <param name="textureUsage">[Optional] The intended usage for the texture.</param>
        /// <param name="spriteCodecs">[Optional] A list of additonal codecs used to read sprite data.</param>
        /// <param name="imageCodecs">[Optional] A list of additonal codecs used to read image data.</param>
        /// <param name="overrideTexture">[Optional] A texture view to use instead of loading the texture from the file system.</param>
        /// <returns>A new <see cref="GorgonSprite"/>, along with its associated texture.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="fileSystem"/>, <paramref name="renderer"/>, or the <paramref name="path"/> parameter is <b>null</b>.</exception>
        /// <exception cref="ArgumentEmptyException">Thrown when the <paramref name="path"/> parameter is empty.</exception>
        /// <exception cref="GorgonException">Thrown if the file system isn't a Gorgon Editor file system, or the file could not be read.</exception>
        /// <remarks>
        /// <para>
        /// This method will load a sprite from a Gorgon Editor file system mounted as a <see cref="IGorgonFileSystem"/>.
        /// </para>
        /// <para>
        /// The <paramref name="spriteCodecs"/> parameter is used to allow custom sprite codecs to be used when loading data (assuming the sprite data was generated using one of the codecs supplied). This
        /// allows a user to create a custom sprite codec plug in and use that to read sprite data.  The <paramref name="imageCodecs"/> is used in exactly the same way, but only for image data.
        /// </para>
        /// <para>
        /// Providing the <paramref name="overrideTexture"/> will skip the texture loading and use the texture passed in.  In this case, the texture return value will be <b>null</b> as it is assumed the
        /// user already knows about the texture resource and is managing the lifetime of the texture elsewhere.
        /// </para>
        /// <para>
        /// When the method returns, it returns a tuple containing the sprite that was loaded, and the associated texture resource for the sprite. If the texture could not be loaded for any reason,
        /// and the <paramref name="overrideTexture"/> parameter is <b>null</b>, then the texture return value will be <b>null</b>, and no texture will be assigned to the sprite.
        /// </para>
        /// <para>
        /// <h2>Technical info</h2>
        /// <para>
        /// Plug ins must generate the following metadata for the files in the editor file system.
        /// </para>
        /// <para>
        /// The sprite file metadata must have the following attributes: <c>Type</c> with a value of "Sprite", and <c>SpriteCodec</c>, and its associated texture must have a dependency type of <c>Image</c> or else the sprite will not load.
        /// </para>
        /// <para>
        /// The associated texture file metadata must have the following attributes: <c>Type</c> with a value of "Image", and <c>ImageCodec</c> or the texure will not load.
        /// </para>
        /// </para>
        /// <para>
        /// <note type="important">
        /// <para>
        /// <b>Regarding textures:</b> This method will load the associated texture for the sprite into memory, and will do its best to only load that texture one time. When the texture is loaded, it will
        /// remain resident until Gorgon is shut down (typically when the application shuts down). In many cases, this is not ideal, so users must dispose of the <see cref="GorgonTexture2D"/> returned by
        /// this method if unloading the texture data is desired (e.g. a level changes and new graphics need to be loaded).
        /// </para>
        /// </note>
        /// </para>
        /// </remarks>
        public static (GorgonSprite sprite, GorgonTexture2D texture) LoadSprite(this IGorgonFileSystem fileSystem, Gorgon2D renderer, string path, ResourceUsage textureUsage = ResourceUsage.Default, IReadOnlyList <IGorgonSpriteCodec> spriteCodecs = null, IReadOnlyList <IGorgonImageCodec> imageCodecs = null, GorgonTexture2DView overrideTexture = null)
        {
            if (fileSystem == null)
            {
                throw new ArgumentNullException(nameof(fileSystem));
            }

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

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

            if (string.IsNullOrWhiteSpace(path))
            {
                throw new ArgumentEmptyException(nameof(path));
            }

            IProjectMetadata metaData = fileSystem.GetMetadata();

            if (!metaData.ProjectItems.TryGetValue(path, out ProjectItemMetadata fileMetadata))
            {
                throw new FileNotFoundException(string.Format(Resources.GOREDIT_ERR_FILE_NOT_FOUND, path));
            }

            IReadOnlyDictionary <string, IGorgonSpriteCodec> supportedSpriteCodecs = GetSpriteCodecs(renderer, spriteCodecs);
            IReadOnlyDictionary <string, IGorgonImageCodec>  supportedImageCodecs  = GetImageCodecs(imageCodecs);

            if ((!fileMetadata.Attributes.TryGetValue(CommonEditorConstants.ContentTypeAttr, out string contentType)) ||
                (!string.Equals(contentType, CommonEditorContentTypes.SpriteType, StringComparison.OrdinalIgnoreCase)))
            {
                throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GOREDIT_ERR_NOT_SPRITE, path));
            }

            if (!fileMetadata.Attributes.TryGetValue("SpriteCodec", out string codecTypeName))
            {
                throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GOREDIT_ERR_UNSUPPORTED_CODEC, string.Empty));
            }

            if (!supportedSpriteCodecs.TryGetValue(codecTypeName, out IGorgonSpriteCodec spriteCodec))
            {
                throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GOREDIT_ERR_UNSUPPORTED_CODEC, codecTypeName));
            }

            IGorgonVirtualFile file = fileSystem.GetFile(path);

            if (file == null)
            {
                throw new FileNotFoundException(string.Format(Resources.GOREDIT_ERR_FILE_NOT_FOUND, path));
            }

            GorgonTexture2D texture = null;

            if (overrideTexture == null)
            {
                if (fileMetadata.DependsOn.TryGetValue(CommonEditorContentTypes.ImageType, out string imagePath))
                {
                    texture         = GetTexture(renderer.Graphics, fileSystem, metaData, imagePath, textureUsage, supportedImageCodecs);
                    overrideTexture = texture.GetShaderResourceView();
                }
            }

            using (Stream stream = file.OpenStream())
            {
                return(spriteCodec.FromStream(stream, overrideTexture, (int)file.Size), texture);
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Function to locate specific types of editor content files contained within the file system.
        /// </summary>
        /// <param name="fileSystem">The file system containing the content items.</param>
        /// <param name="path">Path to the directory containing the files to evaluate.</param>
        /// <param name="contentType">The type of content to locate.</param>
        /// <param name="searchMask">[Optional] A mask for filtering the search by file name.</param>
        /// <param name="recursive">[Optional] <b>true</b> to recursively search, <b>false</b> to only search the specified path.</param>
        /// <returns>A list of <see cref="IGorgonVirtualFile"/> items for each content item.</returns>
        /// <remarks>
        /// <para>
        /// Applications can use this to locate specific types of content files within a file system. If <b>null</b> is passed to the <paramref name="contentType"/>, then all content files with
        /// no content type associated will be returned.
        /// </para>
        /// </remarks>
        public static IReadOnlyList <IGorgonVirtualFile> GetContentItems(this IGorgonFileSystem fileSystem, string path, string contentType, string searchMask = "*", bool recursive = false)
        {
            var result = new List <IGorgonVirtualFile>();

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

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

            if (string.IsNullOrWhiteSpace(path))
            {
                throw new ArgumentEmptyException(nameof(path));
            }

            IProjectMetadata metaData = fileSystem.GetMetadata();

            if (metaData == null)
            {
                return(result);
            }

            // We passed in a file name, extract it for a seach pattern.
            if (string.IsNullOrWhiteSpace(searchMask))
            {
                searchMask = "*";
            }

            path = path.FormatDirectory('/');

            IGorgonVirtualDirectory directory = fileSystem.GetDirectory(path);

            if (directory == null)
            {
                throw new DirectoryNotFoundException();
            }

            IEnumerable <IGorgonVirtualFile> files = fileSystem.FindFiles(directory.FullPath, searchMask, recursive);

            // Handle the unassociated files.
            if (string.IsNullOrWhiteSpace(contentType))
            {
                foreach (IGorgonVirtualFile file in files)
                {
                    if (!metaData.ProjectItems.TryGetValue(file.FullPath, out ProjectItemMetadata metaDataItem))
                    {
                        continue;
                    }

                    IReadOnlyDictionary <string, string> attributes = metaDataItem.Attributes;

                    if ((attributes != null) && (attributes.Count > 0) && (attributes.TryGetValue(CommonEditorConstants.ContentTypeAttr, out string type)))
                    {
                        continue;
                    }

                    result.Add(file);
                }
                return(result);
            }

            // Filter the list based on the content type we ask for.
            foreach (IGorgonVirtualFile file in files)
            {
                if (!metaData.ProjectItems.TryGetValue(file.FullPath, out ProjectItemMetadata metaDataItem))
                {
                    continue;
                }

                IReadOnlyDictionary <string, string> attributes = metaDataItem.Attributes;

                if ((attributes == null) ||
                    (attributes.Count == 0) ||
                    (!attributes.TryGetValue(CommonEditorConstants.ContentTypeAttr, out string type)) ||
                    (!string.Equals(contentType, type, StringComparison.OrdinalIgnoreCase)))
                {
                    continue;
                }

                result.Add(file);
            }

            return(result);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Function to load an image from the editor file system.
        /// </summary>
        /// <param name="fileSystem">The file system containing the editor data.</param>
        /// <param name="path">The path to the sprite.</param>
        /// <param name="imageCodecs">[Optional] A list of additonal codecs used to read image data.</param>
        /// <returns>A new <see cref="IGorgonImage"/> containing the image data from the file system.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="fileSystem"/>, or the <paramref name="path"/> parameter is <b>null</b>.</exception>
        /// <exception cref="ArgumentEmptyException">Thrown when the <paramref name="path"/> parameter is empty.</exception>
        /// <exception cref="GorgonException">Thrown if the file system isn't a Gorgon Editor file system, or the file could not be read.</exception>
        /// <remarks>
        /// <para>
        /// This method will load an image from a Gorgon Editor file system mounted as a <see cref="IGorgonFileSystem"/>.
        /// </para>
        /// <para>
        /// The <paramref name="imageCodecs"/> parameter is used to allow custom image codecs to be used when loading data (assuming the image data was saved using one of the codecs supplied). This
        /// allows a user to create a custom image codec plug in and use that to read image data.
        /// </para>
        /// <para>
        /// <h2>Technical info</h2>
        /// <para>
        /// Plug ins must generate the following metadata for the files in the editor file system.
        /// </para>
        /// <para>
        /// The image file metadata must have the following attributes: <c>Type</c> with a value of "Image", and <c>ImageCodec</c> or else the image will not load.
        /// </para>
        /// <para>
        /// If image file has been marked as premultiplied in the editor, then the texture will be converted to use premultiplied alpha when loading. This is only done when the texture is read from the
        /// file system, cached textures will left as-is.
        /// </para>
        /// </para>
        /// </remarks>
        public static IGorgonImage LoadImage(this IGorgonFileSystem fileSystem, string path, IReadOnlyList <IGorgonImageCodec> imageCodecs = null)
        {
            if (fileSystem == null)
            {
                throw new ArgumentNullException(nameof(fileSystem));
            }

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

            if (string.IsNullOrWhiteSpace(path))
            {
                throw new ArgumentEmptyException(nameof(path));
            }

            IProjectMetadata metaData = fileSystem.GetMetadata();

            if (!metaData.ProjectItems.TryGetValue(path, out ProjectItemMetadata fileMetadata))
            {
                throw new FileNotFoundException(string.Format(Resources.GOREDIT_ERR_FILE_NOT_FOUND, path));
            }

            IReadOnlyDictionary <string, IGorgonImageCodec> supportedImageCodecs = GetImageCodecs(imageCodecs);

            if ((!fileMetadata.Attributes.TryGetValue(CommonEditorConstants.ContentTypeAttr, out string contentType)) ||
                (!string.Equals(contentType, CommonEditorContentTypes.ImageType, StringComparison.OrdinalIgnoreCase)))
            {
                throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GOREDIT_ERR_NOT_IMAGE, path));
            }

            if (!fileMetadata.Attributes.TryGetValue("ImageCodec", out string codecTypeName))
            {
                throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GOREDIT_ERR_UNSUPPORTED_CODEC, string.Empty));
            }

            if (!supportedImageCodecs.TryGetValue(codecTypeName, out IGorgonImageCodec imageCodec))
            {
                throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GOREDIT_ERR_UNSUPPORTED_CODEC, codecTypeName));
            }

            IGorgonVirtualFile file = fileSystem.GetFile(path);

            if (file == null)
            {
                throw new FileNotFoundException(string.Format(Resources.GOREDIT_ERR_FILE_NOT_FOUND, path));
            }

            bool shouldConvertToPremultiplied = false;

            if (fileMetadata.Attributes.TryGetValue("PremultipliedAlpha", out string isPremultiplied))
            {
#pragma warning disable CA1806 // Do not ignore method results
                bool.TryParse(isPremultiplied, out shouldConvertToPremultiplied);
#pragma warning restore CA1806 // Do not ignore method results
            }

            using (Stream stream = file.OpenStream())
            {
                return(shouldConvertToPremultiplied ? imageCodec.LoadFromStream(stream, (int)file.Size).ConvertToPremultipliedAlpha() : imageCodec.LoadFromStream(stream, (int)file.Size));
            }
        }