コード例 #1
0
 /// <summary>Initializes a new instance of the <see cref="T:Gorgon.Editor.SpriteEditor.Services.GorgonSpriteImporter"/> class.</summary>
 /// <param name="sourceFile">The file being imported.</param>
 /// <param name="codec">The original codec for the file.</param>
 /// <param name="renderer">The renderer used to locate the image linked to the sprite.</param>
 /// <param name="fileSystem">The file system containing the file being imported.</param>
 /// <param name="log">The log used for logging debug messages.</param>
 public GorgonSpriteImporter(FileInfo sourceFile, IGorgonSpriteCodec codec, Gorgon2D renderer, IGorgonFileSystem fileSystem, IGorgonLog log)
 {
     _log        = log ?? GorgonLog.NullLog;
     SourceFile  = sourceFile;
     _codec      = codec;
     _renderer   = renderer;
     _fileSystem = fileSystem;
 }
コード例 #2
0
        /// <summary>
        /// Function to copy the contents of a file system to the writable area.
        /// </summary>
        /// <param name="sourceFileSystem">The <see cref="IGorgonFileSystem"/> to copy.</param>
        /// <param name="copyProgress">A method callback used to track the progress of the copy operation.</param>
        /// <param name="allowOverwrite">[Optional] <b>true</b> to allow overwriting of files that already exist in the file system with the same path, <b>false</b> to throw an exception when a file with the same path is encountered.</param>
        /// <returns>A <see cref="ValueTuple{T1,T2}"/> containing the number of directories (<c>item1</c>) and the number of files (<c>item2</c>) copied, or <b>null</b> if the operation was cancelled.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="sourceFileSystem"/> parameter is <b>null</b>.</exception>
        /// <exception cref="IOException">Thrown when the a file exists in <see cref="IGorgonFileSystemWriter{T}.FileSystem"/>, and the <paramref name="allowOverwrite"/> parameter is set to <b>false</b>.</exception>
        /// <remarks>
        /// <para>
        /// This copies all the file and directory information from one file system, into the <see cref="IGorgonFileSystemWriter{T}.FileSystem"/> linked to this writer.
        /// </para>
        /// <para>
        /// When the <paramref name="allowOverwrite"/> is set to <b>false</b>, and a <see cref="IGorgonVirtualFile"/> already exists with the same path as another <see cref="IGorgonVirtualFile"/> in the
        /// <paramref name="sourceFileSystem"/>, then an exception will be raised.
        /// </para>
        /// </remarks>
        public (int DirectoryCount, int FileCount)? CopyFrom(IGorgonFileSystem sourceFileSystem, Func <GorgonWriterCopyProgress, bool> copyProgress = null, bool allowOverwrite = true)
        {
            if (sourceFileSystem == null)
            {
                throw new ArgumentNullException(nameof(sourceFileSystem));
            }

            return(CopyInternal(sourceFileSystem, copyProgress, allowOverwrite, CancellationToken.None));
        }
コード例 #3
0
        /// <summary>
        /// Function to load in the file system for the application.
        /// </summary>
        /// <param name="path">The path to the file system.</param>
        public void Load(string path)
        {
            // Load the file system containing our application data (sprites, images, etc...)
            IGorgonFileSystemProviderFactory providerFactory = new GorgonFileSystemProviderFactory(_plugIns, GorgonApplication.Log);
            IGorgonFileSystemProvider        provider        = providerFactory.CreateProvider("Gorgon.IO.GorPack.GorPackProvider");

            _fileSystem = new GorgonFileSystem(provider, GorgonApplication.Log);

            _fileSystem.Mount(path);
        }
コード例 #4
0
        /// <summary>
        /// Function to locate the associated texture codec and file for a sprite.
        /// </summary>
        /// <param name="fileSystem">The file system to evaluate.</param>
        /// <param name="localDir">The local directory for the sprite file.</param>
        /// <param name="renderer">The renderer used for resource look up.</param>
        /// <param name="textureName">The name of the texture.</param>
        /// <param name="codecs">The list of available image codecs to use when determining texture type.</param>
        /// <returns>A tuple containing the codec, the texture file, and a flag to indicate that the texture was previously loaded into memory.</returns>
        private static (IGorgonImageCodec codec, IGorgonVirtualFile file, bool alreadyLoaded) LocateTextureCodecAndFile(
            IGorgonFileSystem fileSystem,
            IGorgonVirtualDirectory localDir,
            Gorgon2D renderer,
            string textureName,
            IEnumerable <IGorgonImageCodec> codecs)
        {
            // First, attempt to locate the resource by its name.  If it's already loaded, we should not load it again.
            GorgonTexture2D texture = renderer.Graphics
                                      .LocateResourcesByName <GorgonTexture2D>(textureName)
                                      .FirstOrDefault();

            if (texture != null)
            {
                return(null, null, true);
            }

            IGorgonImageCodec codec;

            // We couldn't find the texture in our loaded resources, so try to locate it on the file system.

            // First, check the local directory.
            IEnumerable <IGorgonVirtualFile> files = fileSystem.FindFiles(localDir.FullPath, $"{textureName}.*", false);

            foreach (IGorgonVirtualFile file in files)
            {
                codec = FindTextureCodec(file, codecs);

                if (codec != null)
                {
                    return(codec, file, false);
                }
            }

            // Check to see if the name has path information for the texture in the name.
            // The GorgonEditor from v2 does this.
            if (!textureName.Contains("/"))
            {
                // It is not.  We cannot load the texture.
                return(null, null, false);
            }

            IGorgonVirtualFile textureFile = fileSystem.GetFile(textureName);

            if (textureFile == null)
            {
                return(null, null, false);
            }

            // Try to find a codec for the image file.
            codec = FindTextureCodec(textureFile, codecs);

            return(codec == null ? (null, null, false) : (codec, textureFile, false));
        }
コード例 #5
0
        /// <summary>
        /// Function to asynchronously copy the contents of a file system to the writable area.
        /// </summary>
        /// <param name="sourceFileSystem">The <see cref="IGorgonFileSystem"/> to copy.</param>
        /// <param name="cancelToken">The <see cref="CancellationToken"/> used to cancel an in progress copy.</param>
        /// <param name="copyProgress">A method callback used to track the progress of the copy operation.</param>
        /// <param name="allowOverwrite">[Optional] <b>true</b> to allow overwriting of files that already exist in the file system with the same path, <b>false</b> to throw an exception when a file with the same path is encountered.</param>
        /// <returns>A <see cref="Tuple{T1,T2}"/> containing the number of directories (<c>item1</c>) and the number of files (<c>item2</c>) copied, or <b>null</b> if the operation was cancelled.</returns>
        /// <remarks>
        /// <para>
        /// This copies all the file and directory information from one file system, into the <see cref="IGorgonFileSystemWriter{T}.FileSystem"/> linked to this writer.
        /// </para>
        /// <para>
        /// When the <paramref name="allowOverwrite"/> is set to <b>false</b>, and a <see cref="IGorgonVirtualFile"/> already exists with the same path as another <see cref="IGorgonVirtualFile"/> in the
        /// <paramref name="sourceFileSystem"/>, then an exception will be raised.
        /// </para>
        /// <para>
        /// This version of the copy method allows for an asynchronous copy of a set of a files and directories from another <see cref="IGorgonFileSystem"/>. This method should be used when there is a large
        /// amount of data to transfer between the file systems.
        /// </para>
        /// <para>
        /// Unlike the <see cref="IGorgonFileSystemWriter{T}.CopyFrom"/> method, this method will report the progress of the copy through the <paramref name="copyProgress"/> callback. This callback is a method that takes a
        /// <see cref="GorgonWriterCopyProgress"/> value as a parameter that will report the current state, and will return a <see cref="bool"/> to indicate whether to continue the copy or not (<b>true</b> to
        /// continue, <b>false</b> to stop).
        /// </para>
        /// <para>
        /// <note type="warning">
        /// <para>
        /// The <paramref name="copyProgress"/> method does not switch back to the UI context. Ensure that you invoke any operations that update a UI on the appropriate thread (e.g <c>BeginInvoke</c> on a
        /// WinForms UI element or <c>Dispatcher</c> on a WPF element).
        /// </para>
        /// </note>
        /// </para>
        /// <para>
        /// This method also allows for cancellation of the copy operation by passing a <see cref="CancellationToken"/> to the <paramref name="cancelToken"/> parameter.
        /// </para>
        /// </remarks>
        public Task <(int DirectoryCount, int FileCount)?> CopyFromAsync(IGorgonFileSystem sourceFileSystem, CancellationToken cancelToken, Func <GorgonWriterCopyProgress, bool> copyProgress = null, bool allowOverwrite = true)
        {
            if (sourceFileSystem == null)
            {
                throw new ArgumentNullException(nameof(sourceFileSystem));
            }

            // ReSharper disable MethodSupportsCancellation
            return(Task.Run(() => CopyInternal(sourceFileSystem, copyProgress, allowOverwrite, cancelToken), cancelToken));
            // ReSharper restore MethodSupportsCancellation
        }
コード例 #6
0
        /// <summary>
        /// Function to load the associated sprite texture based on the project metadata.
        /// </summary>
        /// <param name="graphics">The graphics interface to use when looking up or creating the texture.</param>
        /// <param name="fileSystem">The file system containing the file to load.</param>
        /// <param name="metadata">The metadata for the project.</param>
        /// <param name="imagePath">The path to the image.</param>
        /// <param name="usage">The intended usage for the texture.</param>
        /// <param name="codecs">The list of available codecs.</param>
        /// <returns>A new texture containing the sprite texture data.</returns>
        private static GorgonTexture2D GetTexture(GorgonGraphics graphics, IGorgonFileSystem fileSystem, IProjectMetadata metadata, string imagePath, ResourceUsage usage, IReadOnlyDictionary <string, IGorgonImageCodec> codecs)
        {
            bool shouldConvertToPremultiplied = false;

            // First, check to see if this texture isn't already loaded into memory.
            GorgonTexture2D texture = graphics.LocateResourcesByName <GorgonTexture2D>(imagePath).FirstOrDefault();

            if (texture != null)
            {
                return(texture);
            }

            if (!metadata.ProjectItems.TryGetValue(imagePath, out ProjectItemMetadata textureMetadata))
            {
                return(null);
            }

            if (textureMetadata.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
            }

            if ((!textureMetadata.Attributes.TryGetValue(CommonEditorConstants.ContentTypeAttr, out string contentType)) ||
                (!string.Equals(contentType, CommonEditorContentTypes.ImageType, StringComparison.OrdinalIgnoreCase)) ||
                (!textureMetadata.Attributes.TryGetValue("ImageCodec", out string imageCodecTypeName)) ||
                (!codecs.TryGetValue(imageCodecTypeName, out IGorgonImageCodec codec)))
            {
                return(null);
            }

            IGorgonVirtualFile file = fileSystem.GetFile(imagePath);

            if (file == null)
            {
                return(null);
            }

            using (Stream fileStream = file.OpenStream())
            {
                texture = GorgonTexture2D.FromStream(graphics, fileStream, codec, file.Size, new GorgonTexture2DLoadOptions
                {
                    ConvertToPremultipliedAlpha = shouldConvertToPremultiplied,
                    IsTextureCube = false,
                    Name          = file.FullPath,
                    Binding       = TextureBinding.ShaderResource,
                    Usage         = (((usage & ResourceUsage.None) == ResourceUsage.None) || ((usage & ResourceUsage.Staging) == ResourceUsage.Staging)) ? ResourceUsage.Default : usage
                });
            }

            return(texture);
        }
コード例 #7
0
        /// <summary>
        /// Function to copy the contents of a file system to the writable area.
        /// </summary>
        /// <param name="sourceFileSystem">The <see cref="IGorgonFileSystem"/> to copy.</param>
        /// <param name="copyProgress">A method callback used to track the progress of the copy operation.</param>
        /// <param name="allowOverwrite">[Optional] <b>true</b> to allow overwriting of files that already exist in the file system with the same path, <b>false</b> to throw an exception when a file with the same path is encountered.</param>
        /// <returns>A <see cref="ValueTuple{T1,T2}"/> containing the number of directories (<c>item1</c>) and the number of files (<c>item2</c>) copied, or <b>null</b> if the operation was cancelled.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="sourceFileSystem"/> parameter is <b>null</b>.</exception>
        /// <exception cref="IOException">Thrown when the a file exists in <see cref="IGorgonFileSystemWriter{T}.FileSystem"/>, and the <paramref name="allowOverwrite"/> parameter is set to <b>false</b>.</exception>
        /// <remarks>
        /// <para>
        /// This copies all the file and directory information from one file system, into the <see cref="IGorgonFileSystemWriter{T}.FileSystem"/> linked to this writer.
        /// </para>
        /// <para>
        /// When the <paramref name="allowOverwrite"/> is set to <b>false</b>, and a <see cref="IGorgonVirtualFile"/> already exists with the same path as another <see cref="IGorgonVirtualFile"/> in the
        /// <paramref name="sourceFileSystem"/>, then an exception will be raised.
        /// </para>
        /// </remarks>
        public (int DirectoryCount, int FileCount)? CopyFrom(IGorgonFileSystem sourceFileSystem, Func <GorgonWriterCopyProgress, bool> copyProgress = null, bool allowOverwrite = true)
        {
            if (sourceFileSystem == null)
            {
                throw new ArgumentNullException(nameof(sourceFileSystem));
            }

            IGorgonVirtualFile[]      files       = sourceFileSystem.FindFiles("/", "*").ToArray();
            IGorgonVirtualDirectory[] directories = sourceFileSystem.FindDirectories("/", "*").ToArray();

            return((files.Length == 0) && (directories.Length == 0)
                ? ((int DirectoryCount, int FileCount)?)(0, 0)
                       : CopyInternal(copyProgress, allowOverwrite, files, directories, CancellationToken.None));
        }
コード例 #8
0
ファイル: ContentImportPlugin.cs プロジェクト: ishkang/Gorgon
        /// <summary>
        /// Function to open a content object from this plugin.
        /// </summary>
        /// <param name="sourceFile">The file being imported.</param>
        /// <param name="fileSystem">The file system that contains the file being imported.</param>
        /// <returns>A new <see cref="IEditorContent"/> object.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="sourceFile"/> parameter is <b>null</b>.</exception>
        /// <exception cref="GorgonException">Thrown if the <see cref="OnCreateImporter"/> method returns <b>null</b>.</exception>
        public IEditorContentImporter CreateImporter(FileInfo sourceFile, IGorgonFileSystem fileSystem)
        {
            if (sourceFile == null)
            {
                throw new ArgumentNullException(nameof(sourceFile));
            }

            IEditorContentImporter importer = OnCreateImporter(sourceFile, fileSystem);

            if (importer == null)
            {
                throw new GorgonException(GorgonResult.CannotCreate, string.Format(Resources.GOREDIT_ERR_NO_CONTENT_IMPORTER_FROM_PLUGIN, Name));
            }

            return(importer);
        }
コード例 #9
0
        /// <summary>
        /// Function to asynchronously copy the contents of a file system to the writable area.
        /// </summary>
        /// <param name="sourceFileSystem">The <see cref="IGorgonFileSystem"/> to copy.</param>
        /// <param name="cancelToken">The <see cref="CancellationToken"/> used to cancel an in progress copy.</param>
        /// <param name="copyProgress">A method callback used to track the progress of the copy operation.</param>
        /// <param name="allowOverwrite">[Optional] <b>true</b> to allow overwriting of files that already exist in the file system with the same path, <b>false</b> to throw an exception when a file with the same path is encountered.</param>
        /// <returns>A <see cref="ValueTuple{T1,T2}"/> containing the number of directories (<c>item1</c>) and the number of files (<c>item2</c>) copied, or <b>null</b> if the operation was cancelled.</returns>
        /// <remarks>
        /// <para>
        /// This copies all the file and directory information from one file system, into the <see cref="IGorgonFileSystemWriter{T}.FileSystem"/> linked to this writer.
        /// </para>
        /// <para>
        /// When the <paramref name="allowOverwrite"/> is set to <b>false</b>, and a <see cref="IGorgonVirtualFile"/> already exists with the same path as another <see cref="IGorgonVirtualFile"/> in the
        /// <paramref name="sourceFileSystem"/>, then an exception will be raised.
        /// </para>
        /// <para>
        /// This version of the copy method allows for an asynchronous copy of a set of a files and directories from another <see cref="IGorgonFileSystem"/>. This method should be used when there is a large
        /// amount of data to transfer between the file systems.
        /// </para>
        /// <para>
        /// Unlike the <see cref="IGorgonFileSystemWriter{T}.CopyFrom"/> method, this method will report the progress of the copy through the <paramref name="copyProgress"/> callback. This callback is a method that takes a
        /// <see cref="GorgonWriterCopyProgress"/> value as a parameter that will report the current state, and will return a <see cref="bool"/> to indicate whether to continue the copy or not (<b>true</b> to
        /// continue, <b>false</b> to stop).
        /// </para>
        /// <para>
        /// <note type="warning">
        /// <para>
        /// The <paramref name="copyProgress"/> method does not switch back to the UI context. Ensure that you invoke any operations that update a UI on the appropriate thread (e.g <c>BeginInvoke</c> on a
        /// WinForms UI element or <c>Dispatcher</c> on a WPF element).
        /// </para>
        /// </note>
        /// </para>
        /// <para>
        /// This method also allows for cancellation of the copy operation by passing a <see cref="CancellationToken"/> to the <paramref name="cancelToken"/> parameter.
        /// </para>
        /// </remarks>
        public async Task <(int DirectoryCount, int FileCount)?> CopyFromAsync(IGorgonFileSystem sourceFileSystem, CancellationToken cancelToken, Func <GorgonWriterCopyProgress, bool> copyProgress = null, bool allowOverwrite = true)
        {
            if (sourceFileSystem == null)
            {
                throw new ArgumentNullException(nameof(sourceFileSystem));
            }

            // ReSharper disable MethodSupportsCancellation
            (IGorgonVirtualFile[] Files, IGorgonVirtualDirectory[] Directories) = await Task.Run(() =>
            {
                IGorgonVirtualFile[] files            = sourceFileSystem.FindFiles("/", "*").ToArray();
                IGorgonVirtualDirectory[] directories = sourceFileSystem.FindDirectories("/", "*").ToArray();

                return(Files : files, Directories : directories);
            }).ConfigureAwait(false);

            return((Files.Length == 0) && (Directories.Length == 0)
                ? ((int DirectoryCount, int FileCount)?)(0, 0)
                       : await Task.Run(() => CopyInternal(copyProgress, allowOverwrite, Files, Directories, cancelToken), cancelToken).ConfigureAwait(false));

            // ReSharper restore MethodSupportsCancellation
        }
コード例 #10
0
        /// <summary>
        /// Function to load a <see cref="GorgonSprite"/> from a <see cref="GorgonFileSystem"/>.
        /// </summary>
        /// <param name="fileSystem">The file system to load the sprite from.</param>
        /// <param name="renderer">The renderer for the sprite.</param>
        /// <param name="path">The path to the sprite file in the file system.</param>
        /// <param name="textureOptions">[Optional] Options for the texture loaded associated the sprite.</param>
        /// <param name="spriteCodecs">[Optional] The list of sprite codecs to try and load the sprite with.</param>
        /// <param name="imageCodecs">[Optional] The list of image codecs to try and load the sprite texture with.</param>
        /// <returns>The sprite data in the file as a <see cref="GorgonSprite"/>.</returns>
        /// <exception cref="ArgumentNullException">Thrown when the <paramref name="fileSystem"/>, <paramref name="renderer"/>, or <paramref name="path"/> parameter is <b>null</b>.</exception>
        /// <exception cref="ArgumentEmptyException">Thrown when the <paramref name="path"/> parameter is empty.</exception>
        /// <exception cref="FileNotFoundException">Thrown if the file in the <paramref name="path"/> was not found.</exception>
        /// <exception cref="GorgonException">Thrown if the sprite data in the file system could not be loaded because a suitable codec was not found.</exception>
        /// <remarks>
        /// <para>
        /// This method extends a <see cref="GorgonFileSystem"/> so that sprites can be loaded by calling a method on the file system object itself. This negates the need for users to create complex code
        /// for loading a sprite.
        /// </para>
        /// <para>
        /// When loading a sprite, the method will attempt to locate the <see cref="GorgonTexture2DView"/> associated with the sprite (if it exists). When loading, it will check:
        /// <list type="number">
        ///     <item>
        ///         <description>For a texture resource with the same name that is already loaded into memory.</description>
        ///     </item>
        ///     <item>
        ///         <description>Use the local <see cref="IGorgonVirtualDirectory"/> for the sprite file and search for the texture in that directory.</description>
        ///     </item>
        ///     <item>
        ///         <description>Check the entire <paramref name="fileSystem"/> for a file if the texture name contains path information (this is done by the GorgonEditor from v2).</description>
        ///     </item>
        /// </list>
        /// If the file is found, and can be loaded by one of the <paramref name="imageCodecs"/>, then it is loaded and assigned to the sprite.
        /// </para>
        /// <para>
        /// The <paramref name="spriteCodecs"/> is a list of codecs for loading sprite data. If the user specifies this parameter, the only the codecs provided will be used for determining if a sprite can
        /// be read. If it is not supplied, then all built-in (i.e. not plug in based) sprite codecs will be used.
        /// </para>
        /// <para>
        /// The <paramref name="imageCodecs"/> is a list of codecs for loading image data. If the user specifies this parameter, the only the codecs provided will be used for determining if an image can be
        /// read. If it is not supplied, then all built-in (i.e. not plug in based) image codecs will be used.
        /// </para>
        /// </remarks>
        /// <seealso cref="GorgonFileSystem"/>
        /// <seealso cref="GorgonTexture2DView"/>
        /// <seealso cref="GorgonSprite"/>
        public static GorgonSprite LoadSpriteFromFileSystem(this IGorgonFileSystem fileSystem,
                                                            Gorgon2D renderer,
                                                            string path,
                                                            GorgonTexture2DLoadOptions textureOptions     = null,
                                                            IEnumerable <IGorgonSpriteCodec> spriteCodecs = null,
                                                            IEnumerable <IGorgonImageCodec> imageCodecs   = null)
        {
            if (fileSystem == null)
            {
                throw new ArgumentNullException(nameof(fileSystem));
            }

            IGorgonVirtualFile file = fileSystem.GetFile(path);

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

            if ((imageCodecs == null) || (!imageCodecs.Any()))
            {
                // If we don't specify any codecs, then use the built in ones.
                imageCodecs = new IGorgonImageCodec[]
                {
                    new GorgonCodecPng(),
                    new GorgonCodecBmp(),
                    new GorgonCodecDds(),
                    new GorgonCodecGif(),
                    new GorgonCodecJpeg(),
                    new GorgonCodecTga(),
                };
            }
            else
            {
                // Only use codecs that can decode image data.
                imageCodecs = imageCodecs.Where(item => item.CanDecode);
            }

            if ((spriteCodecs == null) || (!spriteCodecs.Any()))
            {
                // Use all built-in codecs if we haven't asked for any.
                spriteCodecs = new IGorgonSpriteCodec[]
                {
                    new GorgonV3SpriteBinaryCodec(renderer),
                    new GorgonV3SpriteJsonCodec(renderer),
                    new GorgonV2SpriteCodec(renderer),
                    new GorgonV1SpriteBinaryCodec(renderer),
                };
            }
            else
            {
                // Only use codecs that can decode sprite data.
                spriteCodecs = spriteCodecs.Where(item => item.CanDecode);
            }

            // We need to copy the sprite data into a memory stream since the underlying stream may not be seekable.
            Stream spriteStream = file.OpenStream();

            try
            {
                if (!spriteStream.CanSeek)
                {
                    Stream newStream = new DataStream((int)spriteStream.Length, true, true);
                    spriteStream.CopyTo(newStream);
                    spriteStream.Dispose();
                    newStream.Position = 0;
                    spriteStream       = newStream;
                }

                IGorgonSpriteCodec spriteCodec = GetSpriteCodec(spriteStream, spriteCodecs);

                if (spriteCodec == null)
                {
                    throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GOR2DIO_ERR_NO_SUITABLE_SPRITE_CODEC_FOUND, path));
                }

                // Try to locate the texture.
                string textureName = spriteCodec.GetAssociatedTextureName(spriteStream);

                GorgonTexture2DView textureForSprite = null;

                // Let's try and load the texture into memory.
                // This does this by:
                // 1. Checking to see if a texture resource with the name specified is already available in memory.
                // 2. Checking the local directory of the file to see if the texture is there.
                // 3. A file system wide search.

                // ReSharper disable once InvertIf
                if (!string.IsNullOrWhiteSpace(textureName))
                {
                    (IGorgonImageCodec codec, IGorgonVirtualFile textureFile, bool loaded) =
                        LocateTextureCodecAndFile(fileSystem, file.Directory, renderer, textureName, imageCodecs);

                    // We have not loaded the texture yet.  Do so now.
                    // ReSharper disable once InvertIf
                    if ((!loaded) && (textureFile != null) && (codec != null))
                    {
                        using (Stream textureStream = textureFile.OpenStream())
                        {
                            textureForSprite = GorgonTexture2DView.FromStream(renderer.Graphics,
                                                                              textureStream,
                                                                              codec,
                                                                              textureFile.Size,
                                                                              GetTextureOptions(textureFile.FullPath, textureOptions));
                        }
                    }
                }

                return(spriteCodec.FromStream(spriteStream, textureForSprite, (int)file.Size));
            }
            finally
            {
                spriteStream?.Dispose();
            }
        }
コード例 #11
0
ファイル: ContentImportPlugin.cs プロジェクト: ishkang/Gorgon
 /// <summary>
 /// Function to open a content object from this plugin.
 /// </summary>
 /// <param name="sourceFile">The file being imported.</param>
 /// <param name="fileSystem">The file system containing the file being imported.</param>
 /// <returns>A new <see cref="IEditorContentImporter"/> object.</returns>
 protected abstract IEditorContentImporter OnCreateImporter(FileInfo sourceFile, IGorgonFileSystem fileSystem);
コード例 #12
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);
            }
        }
コード例 #13
0
        /// <summary>
        /// Function to load the metadata for the project.
        /// </summary>
        /// <param name="fileSystem">The file system we're working with.</param>
        /// <returns>The project metadata interface.</returns>
        internal static IProjectMetadata GetMetadata(this IGorgonFileSystem fileSystem)
        {
            FileInfo           externalProjectData = null;
            IGorgonVirtualFile jsonMetaDataFile    = fileSystem.GetFile(CommonEditorConstants.EditorMetadataFileName);

            // If we're loading directly from an editor file system folder, then check in the directory above the mount point.
            // The editor places all file system data in a directory called "fs", and the metadata is located in the directory above that, so
            // we just need to find the mount point for the "fs" directory, and go up one.
            if (jsonMetaDataFile == null)
            {
                foreach (GorgonFileSystemMountPoint mountPoint in fileSystem.MountPoints.Where(item => item.PhysicalPath.EndsWith(Path.DirectorySeparatorChar + "fs" + Path.DirectorySeparatorChar)))
                {
                    string dirName = Path.GetDirectoryName(mountPoint.PhysicalPath.Substring(0, mountPoint.PhysicalPath.Length - 1));
                    if (File.Exists(Path.Combine(dirName, CommonEditorConstants.EditorMetadataFileName)))
                    {
                        externalProjectData = new FileInfo(Path.Combine(dirName, CommonEditorConstants.EditorMetadataFileName));
                    }
                }

                if (externalProjectData == null)
                {
                    throw new GorgonException(GorgonResult.CannotRead, Resources.GOREDIT_ERR_NOT_EDITOR_PROJECT);
                }
            }

            int expectedVersion = Convert.ToInt32(CommonEditorConstants.EditorCurrentProjectVersion.Replace("GOREDIT", string.Empty));
            int fileVersion     = int.MaxValue;

            using (Stream stream = externalProjectData == null ? jsonMetaDataFile.OpenStream() : externalProjectData.Open(FileMode.Open, FileAccess.Read, FileShare.Read))
                using (var reader = new StreamReader(stream, Encoding.UTF8))
                    using (var jsonReader = new JsonTextReader(reader))
                    {
                        // First property must be the version #.
                        if ((!jsonReader.Read()) || (!jsonReader.Read()))
                        {
                            throw new GorgonException(GorgonResult.CannotRead, Resources.GOREDIT_ERR_NOT_EDITOR_PROJECT);
                        }

                        if ((jsonReader.TokenType != JsonToken.PropertyName) ||
                            (!string.Equals(jsonReader.Value.ToString(), nameof(IProjectMetadata.Version), StringComparison.Ordinal)))
                        {
                            throw new GorgonException(GorgonResult.CannotRead, Resources.GOREDIT_ERR_NOT_EDITOR_PROJECT);
                        }

                        if (!int.TryParse(jsonReader.ReadAsString().Replace("GOREDIT", string.Empty), NumberStyles.Integer, CultureInfo.InvariantCulture, out fileVersion))
                        {
                            throw new GorgonException(GorgonResult.CannotRead, Resources.GOREDIT_ERR_NOT_EDITOR_PROJECT);
                        }

                        // Ensure we have the correct version.
                        if (expectedVersion < fileVersion)
                        {
                            throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GOREDIT_ERR_VERSION_MISMATCH, fileVersion, expectedVersion));
                        }
                    }

            using (Stream stream = externalProjectData == null ? jsonMetaDataFile.OpenStream() : externalProjectData.Open(FileMode.Open, FileAccess.Read, FileShare.Read))
                using (var reader = new StreamReader(stream, Encoding.UTF8))
                {
                    string jsonString = reader.ReadToEnd();

                    switch (fileVersion)
                    {
                    case 30:
                        return(JsonConvert.DeserializeObject <EditorProjectMetadata30>(jsonString));

                    default:
                        throw new GorgonException(GorgonResult.CannotRead, string.Format(Resources.GOREDIT_ERR_VERSION_MISMATCH, fileVersion, expectedVersion));
                    }
                }
        }
コード例 #14
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);
        }
コード例 #15
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));
            }
        }
コード例 #16
0
 /// <summary>Function to open a content object from this plugin.</summary>
 /// <param name="sourceFile">The file being imported.</param>
 /// <param name="fileSystem">The file system containing the file being imported.</param>
 /// <param name="log">The logging interface to use.</param>
 /// <returns>A new <see cref="IEditorContentImporter"/> object.</returns>
 protected override IEditorContentImporter OnCreateImporter(FileInfo sourceFile, IGorgonFileSystem fileSystem) => new DdsImageImporter(sourceFile, GetCodec(sourceFile), CommonServices.Log);
コード例 #17
0
        /// <summary>
        /// Function to copy data from a file system to the file system linked to this writer.
        /// </summary>
        /// <param name="sourceFileSystem">The file system to copy from.</param>
        /// <param name="progress">The callback for copy progress.</param>
        /// <param name="allowOverwrite">Flag to indicate whether to allow overwriting files or not.</param>
        /// <param name="token">The cancellation token for asynchronous copy.</param>
        /// <returns>A tuple containing the count of the directories and files copied.</returns>
        private (int DirectoryCount, int FileCount)? CopyInternal(IGorgonFileSystem sourceFileSystem,
                                                                  Func <GorgonWriterCopyProgress, bool> progress,
                                                                  bool allowOverwrite,
                                                                  CancellationToken token)
        {
            int directoryCount = 0;
            int fileCount      = 0;

            // Enumerate files and directories from the source.
            IGorgonVirtualFile[]      files       = sourceFileSystem.FindFiles("/", "*").ToArray();
            IGorgonVirtualDirectory[] directories = sourceFileSystem.FindDirectories("/", "*").ToArray();

            if ((files.Length == 0) && (directories.Length == 0))
            {
                return(0, 0);
            }

            // Create all the directories.
            foreach (IGorgonVirtualDirectory directory in directories)
            {
                if (token.IsCancellationRequested)
                {
                    return(null);
                }

                CreateDirectory(directory.FullPath);
                ++directoryCount;
            }

            foreach (IGorgonVirtualFile file in files)
            {
                IGorgonVirtualFile destFile = FileSystem.GetFile(file.FullPath);

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

                // Do not allow overwrite if the user requested it.
                if ((destFile != null) && (!allowOverwrite))
                {
                    throw new IOException(string.Format(Resources.GORFS_ERR_FILE_EXISTS, destFile.FullPath));
                }

                // Copy the file.
                using (Stream sourceStream = file.OpenStream())
                {
                    using (Stream destStream = OpenStream(file.FullPath, FileMode.Create))
                    {
                        sourceStream.CopyTo(destStream, 80000);
                    }
                }

                ++fileCount;

                if (progress == null)
                {
                    continue;
                }

                if (!progress(new GorgonWriterCopyProgress(file, fileCount, files.Length, directories.Length)))
                {
                    return(null);
                }
            }

            return(directoryCount, fileCount);
        }