I'm trying to implement a system for a game editor wherein it will load a predefined texture in the library. The texture uses a proprietary format that the library is already capable of reading, but currently only supports doing so using a pointer address and length as input.
I've currently gotten it working by moving the texture into a temporary file, but this is a fairly unoptimized workaround. I was wondering if there was any way in particular to get the address of the internal file, either with or without the stream. Here is my current (working) code, I can provide additional information on how NodeFactory works if necessary but the main thing is that it requires an address and length to work properly.
var resourceName = ("BrawlLib.HardcodedFiles.metal00.tex0");
string listDefault = "";
using (Stream stream = assembly.GetManifestResourceStream(resourceName))
{
string file;
FileStream fstream = new FileStream((file = Path.GetTempFileName()), FileMode.OpenOrCreate);
stream.CopyTo(fstream);
tNode = NodeFactory.FromFile(null, file) as TEX0Node;
Texture.Attach(tNode, _palette);
}
Related
I'm struggling a little with images on the Azure platform under dotnet core and I'm hoping someone can make a sensible suggestion.
Simple enough premise: user uploads image, saved in a database as base64 (about to move to Azure storage blob, but that's irrelevant to this). Later on, site owner comes along and clicks a button to get all these images down in a ZIP file. In the old days of .net framework this was easy. Now I seem to be hitting a set of 'yes, but' comments.
Yes, there's system.drawing.image but you can't use that because it's not in dotnet core (until recently).
Yes, you can use CoreCompat but it doesn't work on Azure because in Web Applications there's no support for GDI+.
Yes, even if I could, I'm developing on a Mac so it won't work locally as far as I can see.
I have tried beta4 of ImageSharp without a lot of success. It's random - sometimes it works, sometimes it just throws OutOfMemoryException.
I have tried SkiaSharp but similar results; sometimes it works, sometimes it spits out random error messages.
I'm not doing anything fancy in terms of processing, no resizing or anything. It should be a case of load file to byte array from Convert.FromBase64String, create Zip file entry, ultimately spit out zip file. The ZIP portion is fine, but I need something decent that can do the image work.
Here's a bit of code:
if(!string.IsNullOrEmpty(del.Headshot))
{
var output=SKImage.FromBitmap(SKBitmap.Decode(Convert.FromBase64String(del.Headshot)));
MemoryStream savedFile=new MemoryStream();
output.Encode(SKEncodedImageFormat.Jpeg, 100).SaveTo(savedFile);
string name=$"{del.FirstName} {del.LastName} - {del.Company}".Trim(Path.GetInvalidFileNameChars()) + "_Headshot.jpg";
ZipArchiveEntry entry=zip.CreateEntry(name);
using(Stream entryStream=entry.Open())
{
entryStream.Write(savedFile.ToArray(), 0, Convert.ToInt32(savedFile.Length));
}
output.Dispose();
savedFile.Dispose();
}
Can anyone give me a sensible suggestion for a library that can handle images, cross-platform and on Azure, before I pull out what little hair remains!
Thanks
EDIT: The first answer is technically correct, I don't need anything else. However, I might have been a bit wrong when I said I wasn't doing any image manipulation. Because it's all base64 without a filename being stored anywhere, I've no idea what sort of file it is. I'm therefore saving each one as JPEG to ensure that I can always output that file type and extension. Users I guess could be uploading JPG / PNG or even GIF.
Technically you do not need any of those other imaging (unless you are doing more that just zipping the content). Convert the base64 to byte array and pass that to the zip file. No need to save to disk just to read it back again for zipping.
//...
if(!string.IsNullOrEmpty(del.Headshot)) {
var imageBytes = Convert.FromBase64String(del.Headshot);
string name = $"{del.FirstName} {del.LastName} - {del.Company}".Trim(Path.GetInvalidFileNameChars()) + "_Headshot.jpg";
ZipArchiveEntry entry = zip.CreateEntry(name);
using(Stream entryStream = entry.Open()) {
entryStream.Write(imageBytes, 0, imageBytes.Length));
}
}
//...
Also using a minor hack for known image types when converted to base64
public static class ImagesUtility {
static IDictionary<string, string> mimeMap =
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "IVBOR", "png" },
{ "/9J/4", "jpg" },
//...add others
};
/// <summary>
/// Extract image file extension from base64 string.
/// </summary>
/// <param name="base64String">base64 string.</param>
/// <returns>file extension from string.</returns>
public static string GetFileExtension(string base64String) {
var data = base64String.Substring(0, 5);
var extension = mimeMap[data.ToUpper()];
return extension;
}
}
You could try to determine the file extension from its prefix
if(!string.IsNullOrEmpty(del.Headshot)) {
var imageBytes = Convert.FromBase64String(del.Headshot);
var ext = ImagesUtility.GetFileExtension(del.Headshot) ?? "jpg";
string name = $"{del.FirstName} {del.LastName} - {del.Company}".Trim(Path.GetInvalidFileNameChars()) + $"_Headshot.{ext}";
ZipArchiveEntry entry = zip.CreateEntry(name);
using(Stream entryStream = entry.Open()) {
entryStream.Write(imageBytes, 0, imageBytes.Length));
}
}
Now ideally, if you are able to control the type of image uploaded, then you should also validate and do what ever image processing when the data is being saved along with any needed metadata (ie content type). That way when extracting it from storage, you can be confident that it is the correct type and size. That saves you having to worry about that later on.
Aspose.Drawing and Aspose.Imaging can handle images and cross-platform running on .NET Core (I'm one of the developers).
I have build a program which allows me to insert comment and the title of an Image through System.Image.Drawing so right now, I have trouble trying to overwrite the existing Jpeg file with the one that has comment and title added into it, but I am encountering error while deleting the file, so I'm not sure what to do as I have tried disposing the file but I cant saved it in that case, due to the fact that I disposed it too early, but I cant saved it because the existing file name is not deleted so I'm kinda stuck in the middle right now.
Here are my codes for it:
public string EditComment(string OriginalFilepath, string newFilename)
{
image = System.Drawing.Image.FromFile(OriginalFilepath);
PropertyItem propItem = image.PropertyItems[0];
using (var file = System.Drawing.Image.FromFile(OriginalFilepath))
{
propItem.Id = 0x9286; // this is the id for 'UserComment'
propItem.Type = 2;
propItem.Value = System.Text.Encoding.UTF8.GetBytes("HelloWorld\0");
propItem.Len = propItem.Value.Length;
file.SetPropertyItem(propItem);
PropertyItem propItem1 = file.PropertyItems[file.PropertyItems.Count() - 1];
file.Dispose();
image.Dispose();
string filepath = Filepath;
if (File.Exists(#"C:\Desktop\Metadata"))
{
System.IO.File.Delete(#"C:\Desktop\Metadata");
}
string newFilepath = filepath + newFilename;
file.Save(newFilepath, ImageFormat.Jpeg);//error appears here
return filepath;
}
}
The Error shown are:
An exception of type 'System.ArgumentException' occurred in System.Drawing.dll but was not handled in user code
Additional information: Parameter is not valid.
The problem is that opening an image from file locks the file. You can get around that by reading the file into a byte array, creating a memory stream from that, and then opening the image from that stream:
public string EditComment(string originalFilepath, string newFilename)
{
Byte[] bytes = File.ReadAllBytes(originalFilepath);
using (MemoryStream stream = new MemoryStream(bytes))
using (Bitmap image = new Bitmap(stream))
{
PropertyItem propItem = image.PropertyItems[0];
// Processing code
propItem.Id = 0x9286; // this is the id for 'UserComment'
propItem.Type = 2;
propItem.Value = System.Text.Encoding.UTF8.GetBytes("HelloWorld\0");
propItem.Len = propItem.Value.Length;
image.SetPropertyItem(propItem);
// Not sure where your FilePath comes from but I'm just
// putting it in the same folder with the new name.
String newFilepath;
if (newFilename == null)
newFilepath = originalFilePath;
else
newFilepath = Path.Combine(Path.GetDirectory(originalFilepath), newFilename);
image.Save(newFilepath, ImageFormat.Jpeg);
return newFilepath;
}
}
Make sure you do not dispose your image object inside the using block as you did in your test code. Not only does the using block exist exactly so you don't have to dispose manually, but it's also rather hard to save an image to disk that no longer exists in memory. Similarly, you seem to open the image from file twice. I'm just going to assume all of those were experiments to try to get around the problem, but do make sure to clean those up.
The basic rules to remember when opening images are these:
An Image object created from a file will lock the file during the life cycle of the image object, preventing the file from being overwritten or deleted until the image is disposed.
An Image object created from a stream will need the stream to remain open for the entire life cycle of the image object. Unlike with files, there is nothing actively enforcing this, but after the stream is closed, the image will give errors when saved, cloned or otherwise manipulated.
Contrary to what some people believe, a basic .Clone() call on the image object will not change this behaviour. The cloned object will still keep the reference to the original source.
Note, if you actually want a usable image object that is not contained in a using block, you can use LockBits and Marshal.Copy to copy the byte data of the image object into a new image with the same dimensions and the same PixelFormat, effectively making a full data clone of the original image. (Note: I don't think this works on animated GIF files) After doing that, you can safely dispose the original and just use the new cleanly-cloned version.
There are some other workarounds for actually getting the image out, but most of them I've seen aren't optimal. These are the two most common other valid workarounds for the locking problem:
Create a new Bitmap from an image loaded from file using the Bitmap(Image image) constructor. This new object will not have the link to that file, leaving you free to dispose the one that's locking the file. This works perfectly, but it changes the image's colour depth to 32-bit ARGB, which might not be desired. If you just need to show an image on the UI, this is an excellent solution, though.
Create a MemoryStream as shown in my code, but not in a using block, leaving the stream open as required. Leaving streams open doesn't really seem like a good idea to me. Though some people have said that, since a MemoryStream is just backed by a simple array, and not some external resource, the garbage collector apparently handles this case fine...
I've also seen some people use System.Drawing.ImageConverter to convert from bytes, but I looked into the internals of that process, and what it does is actually identical to the last method here, which leaves a memory stream open.
Basically I have a binary file which was created using the c++ memory mapped library. I have all the information regarding the struct in c++. I am trying to read that binary file using memory mapped library in c#. Can someone show me any articles/blogs which show how to achieve that?
I am struggling at mapping the c++ struct to c# struct and also the steps required to read and map the content.
Thanks in advance.
First, open the file with a FileStream.
var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
Then instantiate a BinaryReader from that stream. This isn't completely required, but makes working with binary data easier.
var reader = new BinaryReader(stream);
Next, read in as many bytes as the structure needs, as indicated by Marshal.SizeOf:
var bytes = reader.ReadBytes(Marshal.SizeOf(typeof (MyStruct)));
Then, we "pin" that managed memory so the GC doesn't move it, using GCHandle.Alloc:
var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
Finally, we convert that buffer to a managed structure, using Marshal.PtrToStructure:
var myStruct = (MyStruct)Marshal.PtrToStructure(
handle.AddrOfPinnedObject(), typeof (MyStruct));
Don't forget to free the GCHandle:
handle.Free();
return theStructure;
}
Here is an example of part of that wrapped up in a generic function.
While I haven't tried it, I'm fairly confident this would also work with a using a MemoryMappedFile and calling CreateViewStream (which you pass to the BinaryReader.)
var mmfile = MemoryMappedFile.CreateFromFile(path);
var stream = mmfile.CreateViewStream();
var reader = new BinaryReader(stream);
// ...
I have a code written in MFC for file reading and writing. I am rewriting it for C#.
My file consists of three parts, Header *Body* and Footer. Now in MFC code CArchive can write/read any of these parts. This is done by COleStreamFile::OpenStream. In this method we gave which part to read and this returns stream pointing to that location in the file. CArchive then uses stream and reads/writes to the file.
COleStreamFile stream;
//Stream is pointed to footer location.
stream.OpenStream(m_pStg, "Footer", nOpenFlags, pError); // pStg is LPSTORAGE
CArchive ar(&stream, CArchive::load);
Now after this code when I do ar >> or ar << I didn't read file from start. It is reading from middle or end (depends on stream). Now what I want is that to convert this code to C#. Whats the replacement for COleStreamFile::OpenStream in C#.
Here is what I have done so far.
using (var stream = new FileStream(filePath, FileMode.Open))
{
using (var binaryReader = new BinaryReader(stream)
{
}
}
Now here stream is pointing to the start. I think I can give it to read from specific byte. But I don't know that byte location. What I know is Header, Body and Footer names which are being used by MFC code.
Or is there any way to find out the current location of CArchive when it is reading or writing. If I get byte location from there I can use that as well.
I am using icsharpziplib dll for zipping sharepoint files using c# in asp.net
When i open the output.zip file, it is showing "zip file is either corrupted or damaged".
And the crc value for files in the output.zip is showing as 000000.
How do we calculate or configure crc value using icsharpziplib dll?
Can any one have the good example how to do zipping using memorystreams?
it seems you're not creating each ZipEntry.
Here's is a code that I adapted to my needs:
http://wiki.sharpdevelop.net/SharpZipLib-Zip-Samples.ashx#Create_a_Zip_fromto_a_memory_stream_or_byte_array_1
Anyway with SharpZipLib there are many ways you can work with zip file: the ZipFile class, the ZipOutputStream and the FastZip.
I'm using the ZipOutputStream to create an in-memory ZIP file, adding in-memory streams to it and finally flushing to disk, and it's working quite good. Why ZipOutputStream? Because it's the only choice available if you want to specify a compression level and use Streams.
Good luck :)
1:
You could do it manually but the ICSharpCode library will take care of it for you. Also something I've discovered: 'zip file is either corrupted or damaged' can also be a result of not adding your zip entry name correctly (such as an entry that sits in a chain of subfolders).
2:
I solved this problem by creating a compressionHelper utility. I had to dynamically compose and return zip files. Temp files were not an option as the process was to be run by a webservice.
The trick with this was a BeginZip(), AddEntry() and EndZip() methods (because I made it into a utility to be invoked. You could just use the code directly if need be).
Something I've excluded from the example are checks for initialization (like calling EndZip() first by mistake) and proper disposal code (best to implement IDisposable and close your zipfileStream and your memoryStream if applicable).
using System.IO;
using ICSharpCode.SharpZipLib.Zip;
public void BeginZipUpdate()
{
_memoryStream = new MemoryStream(200);
_zipOutputStream = new ZipOutputStream(_memoryStream);
}
public void EndZipUpdate()
{
_zipOutputStream.Finish();
_zipOutputStream.Close();
_zipOutputStream = null;
}
//Entry name could be 'somefile.txt' or 'Assemblies\MyAssembly.dll' to indicate a folder.
//Unsure where you'd be getting your file, I'm reading the data from the database.
public void AddEntry(string entryName, byte[] bytes)
{
ZipEntry entry = new ZipEntry(entryName);
entry.DateTime = DateTime.Now;
entry.Size = bytes.Length;
_zipOutputStream.PutNextEntry(entry);
_zipOutputStream.Write(bytes, 0, bytes.Length);
_zipOutputStreamEntries.Add(entryName);
}
So you're actually having the zipOutputStream write to a memoryStream. Then once _zipOutputStream is closed, you can return the contents of the memoryStream.
public byte[] GetResultingZipFile()
{
_zipOutputStream.Finish();
_zipOutputStream.Close();
_zipOutputStream = null;
return _memoryStream.ToArray();
}
Just be aware of how much you want to add to a zipfile (delay in process/IO/timeouts etc).