Implemented data: ser/des.
This commit is contained in:
parent
10d912d302
commit
e196d5f50f
@ -37,7 +37,7 @@ namespace ReFuel.Gltf
|
||||
return File.OpenRead(path);
|
||||
}
|
||||
case "data":
|
||||
return new GltfUriReader(Uri);
|
||||
return DataUriStream.FromUri(Uri.OriginalString);
|
||||
default:
|
||||
if (provider != null)
|
||||
return provider.OpenFile(pwd, Uri) ?? throw new FileNotFoundException(Uri.ToString());
|
||||
|
@ -33,7 +33,7 @@ namespace ReFuel.Gltf
|
||||
internal GltfImage(GltfDocument document) : base(document)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public Stream Open(string? pwd = null, IGltfStreamProvider? provider = null)
|
||||
{
|
||||
if (Uri == null)
|
||||
@ -52,16 +52,16 @@ namespace ReFuel.Gltf
|
||||
}
|
||||
else
|
||||
{
|
||||
string path = Path.Combine(pwd ?? string.Empty, Uri.LocalPath);
|
||||
string path = Path.Combine(pwd ?? string.Empty, Uri.LocalPath);
|
||||
return File.OpenRead(path);
|
||||
}
|
||||
case "data":
|
||||
return new GltfUriReader(Uri);
|
||||
return DataUriStream.FromUri(Uri.OriginalString);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// otherwise
|
||||
|
||||
|
||||
if (provider != null)
|
||||
return provider.OpenFile(pwd, Uri) ?? throw new FileNotFoundException(Uri.ToString());
|
||||
else
|
||||
@ -102,4 +102,4 @@ namespace ReFuel.Gltf
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
99
ReFuel.Gltf/IO/DataUriStream.cs
Executable file
99
ReFuel.Gltf/IO/DataUriStream.cs
Executable file
@ -0,0 +1,99 @@
|
||||
using System.Text;
|
||||
|
||||
namespace ReFuel.Gltf.IO
|
||||
{
|
||||
public class DataUriStream : MemoryStream
|
||||
{
|
||||
public string? MediaType { get; set; }
|
||||
|
||||
public DataUriStream()
|
||||
{
|
||||
}
|
||||
|
||||
public DataUriStream(byte[] array) : base(array, true)
|
||||
{
|
||||
}
|
||||
|
||||
public DataUriStream(int capacity) : base(capacity)
|
||||
{
|
||||
}
|
||||
|
||||
public string ToUri(Encoding? encoding = null)
|
||||
{
|
||||
// Not the most efficient implementation here but it works.
|
||||
StringBuilder builder = new StringBuilder((int)Length * 4 / 3);
|
||||
ReadOnlySpan<byte> buffer = GetBuffer().AsSpan()[..(int)Length];
|
||||
|
||||
if (MediaType != null)
|
||||
{
|
||||
builder.Append(MediaType);
|
||||
}
|
||||
|
||||
if (encoding == null)
|
||||
{
|
||||
builder.Append(";base64,");
|
||||
builder.Append(Convert.ToBase64String(buffer));
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append(';');
|
||||
builder.Append(encoding.WebName);
|
||||
builder.Append(',');
|
||||
builder.Append(Uri.EscapeDataString(encoding.GetString(buffer)));
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
public static DataUriStream FromUri(ReadOnlySpan<char> uri)
|
||||
{
|
||||
if (uri.CompareTo("data:", StringComparison.InvariantCulture) != 0)
|
||||
{
|
||||
throw new Exception("Expected a data uri.");
|
||||
}
|
||||
|
||||
int commaIndex = uri.IndexOf(',');
|
||||
|
||||
string? mediaType = null;
|
||||
string dataType = "base64";
|
||||
ReadOnlySpan<char> data;
|
||||
|
||||
if (commaIndex != -1)
|
||||
{
|
||||
ReadOnlySpan<char> header = uri[5..commaIndex];
|
||||
int lastSemicolon = header.LastIndexOf(';');
|
||||
ReadOnlySpan<char> mediaTypeSpan = header[..lastSemicolon];
|
||||
ReadOnlySpan<char> dataTypeSpan = header[(lastSemicolon + 1)..];
|
||||
|
||||
mediaType = mediaTypeSpan.ToString();
|
||||
dataType = dataTypeSpan.ToString();
|
||||
data = uri[(commaIndex + 1)..];
|
||||
}
|
||||
else
|
||||
{
|
||||
data = uri[5..];
|
||||
}
|
||||
|
||||
|
||||
DataUriStream stream;
|
||||
if (dataType == "base64")
|
||||
{
|
||||
stream = new DataUriStream(data.Length * 3 / 4 + 1) { MediaType = mediaType };
|
||||
|
||||
if (!Convert.TryFromBase64Chars(data, stream.GetBuffer(), out int bytesWritten))
|
||||
{
|
||||
throw new Exception("Base64 conversion failed.");
|
||||
}
|
||||
|
||||
stream.SetLength(bytesWritten);
|
||||
}
|
||||
else
|
||||
{
|
||||
Encoding encoding = Encoding.GetEncoding(dataType);
|
||||
stream = new DataUriStream(encoding.GetBytes(Uri.UnescapeDataString(data.ToString())));
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
namespace ReFuel.Gltf.IO
|
||||
{
|
||||
public class GltfUriReader : Stream
|
||||
{
|
||||
public Uri Uri { get; }
|
||||
public string Type { get; }
|
||||
public string Encoding { get; }
|
||||
|
||||
private string data;
|
||||
private int index = 0;
|
||||
|
||||
public GltfUriReader(Uri uri)
|
||||
{
|
||||
if (uri.Scheme != "data")
|
||||
throw new Exception("Expected a URI with 'data:' scheme.");
|
||||
|
||||
Uri = uri;
|
||||
data = uri.AbsolutePath;
|
||||
index = data.IndexOf(',') + 1;
|
||||
|
||||
if (index == 0)
|
||||
{
|
||||
throw new Exception("No data string.");
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanRead => true;
|
||||
|
||||
public override bool CanSeek => true;
|
||||
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override long Length => throw new NotImplementedException();
|
||||
|
||||
public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user