Unity Resources.load() won't work with external dll - c#

I'm trying to load an asset using Resources.load() but it always returns null.
Here is my folder structure: https://imgur.com/a/z8KObW1
When I use Resources.load() in my unity project, it works without any problem.
But when I create a seperate project in visual studio with the unityengine dll's, and use Resources.load() in one of the .cs files under the Source folder it always seems to return null, no matter what I try.
When I placed the Assets folder inside of the Source folder it returned null and when I placed the .cs files in the Asset folder it also returned null. I can't seem to figure out why. I also tried to fetch the path that the Resources.load() starts at, but I can't seem to figure it out. Does it start from the .dll or from the .cs file under the Source?
public static void Create_Manager() {
GameObject networkManagerObj = new GameObject();
networkManagerObj.name = "_NetworkManager";
networkManager = networkManagerObj.AddComponent<NetworkManager>();
networkManager.playerPrefab = Resources.Load("Player") as GameObject;
networkManager.dontDestroyOnLoad = true;
networkManager.runInBackground = true;
networkManager.autoCreatePlayer = true;
}
All I need is for the Resources.load() to work with my seperate project/dll. Does anyone know how to do so?

Here's the code that a coworker of mine wrote and mostly explained to me. I've omitted a lot of the code that dealt with what this class was for (handling touch input), leaving only the sections related to loading an image (used to display a touch point on screen).
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEngine;
using HedgehogTeam.EasyTouch;
namespace TouchControls
{
/// <summary>Helper to control touch-based input.</summary>
public static class Utility
{
const string imageNamespace = "TouchControls.";
const string imageResourceFolder = "TouchControls/Images/";
/// <summary>Static helper to contain texture resources.</summary>
public static class Texture
{
/// <summary>The texture used to represent a second finger when simulating touches.</summary>
public static Texture2D SecondFinger
{
get
{
if (null == secondFinger)
secondFinger = LoadImage(secondFingerFilename);
return secondFinger;
}
}
static Texture2D secondFinger = null;
const string secondFingerFilename = "secondFinger.png";
}
static Assembly ExecutingAssembly
{
get
{
if (null == executingAssembly)
{
executingAssembly = Assembly.GetExecutingAssembly();
}
return executingAssembly;
}
}
static Assembly executingAssembly = null;
static Stream GetImageStream(string imageName) { return ExecutingAssembly.GetManifestResourceStream(imageName); }
static Texture2D LoadImage(string filename, TextureWrapMode wrapMode = TextureWrapMode.Clamp, FilterMode filterMode = FilterMode.Bilinear, bool useMipMaps = true, TextureFormat format = TextureFormat.ARGB32)
{
Texture2D texture = Resources.Load<Texture2D>(imageResourceFolder + Path.GetFileNameWithoutExtension(!string.IsNullOrEmpty(filename) ? filename : string.Empty));
try
{
// Didn't find it in resources in the project so try to find it in the library manifest....
if (null == texture)
{
using (Stream stream = GetImageStream(imageNamespace + filename))
{
texture = new Texture2D(0, 0, format, useMipMaps);
if (!texture.LoadImage(GetImageBuffer(stream)))
throw new NotSupportedException(filename);
texture.wrapMode = wrapMode;
texture.filterMode = filterMode;
}
}
else // ensure it is read/write enabled...
{
Texture2D invertedTexture = new Texture2D(texture.width, texture.height, texture.format, 1 < texture.mipmapCount);
invertedTexture.SetPixels32(texture.GetPixels32());
invertedTexture.Apply(true);
texture = invertedTexture;
}
}
catch
{
// Something went wrong so make a magenta 4 pixel texture.
texture = new Texture2D(2, 2, TextureFormat.ARGB32, false);
texture.SetPixels(0, 0, 2, 2, Enumerable.Repeat(Color.magenta, 4).ToArray());
}
texture.Apply(true);
return texture;
}
static byte[] GetImageBuffer(Stream stream)
{
stream.Seek(0, SeekOrigin.Begin);
byte[] buffer = new byte[stream.Length];
stream.Read(buffer, 0, (int)stream.Length);
return buffer;
}
}
}
It first attempts to load an image out of the Resources folder (as you're familiar with) at a given location: this allows the image to be overridden by the local project, if desired. If that image isn't found, then it loads one out of the DLL. I am not sure where this image is located, but as the path reference is in the code, I'm sure it works out sensibly enough (the key part he had informed me was how to insure that the file got included in the DLL, rather than where it was located).
The important chunk for loading it out of the DLL is this section:
static Stream GetImageStream(string imageName) { return ExecutingAssembly.GetManifestResourceStream(imageName); }
//...
using (Stream stream = GetImageStream(imageNamespace + filename))
{
texture = new Texture2D(0, 0, format, useMipMaps);
if (!texture.LoadImage(GetImageBuffer(stream)))
throw new NotSupportedException(filename);
texture.wrapMode = wrapMode;
texture.filterMode = filterMode;
}
//...
texture.Apply(true);
return texture;

Related

Save an Image with superimposed Label on the device

I have some images in my ftp web space, and through this method I can view them on the screen, and above them I insert a Label.
var ImageUrl = "ftp://xxxxxxxxx.jpg";
//Download Image
byte[] ImgByte1 = WebClient.DownloadData(ImageUrl);
MemoryStream mStream1 = new MemoryStream(ImgByte1);
ObservableCollection<FraseClass> listFrasi = new ObservableCollection<FraseClass>
{
new FraseClass{Source=ImageSource.FromStream(() => mStream1)},
}
XAML
<Image
Source="{Binding Source}"/>
<Label
Text="Hello"/>
I am looking for a way to be able to save the image on the device with the text superimposed. I tried to search but couldn't find anything to fix my problem
I am looking for a way to be able to save the image on the device with the text superimposed. I tried to search but couldn't find anything to fix my problem
According to your description, you want to save image that downloadind from url into local storage, am I right?
If yes, I suggest you can use Xamarin.Forms DependencyService to do this.
Firstly, create an interface in shared code.
public interface IImageFile
{
void SaveImage(string name, byte[] data, string location = "temp");
}
Then implement the interface on Android platform. Don't forget to register the platform implementations.
[assembly: Dependency(typeof(ImageFile))]
namespace FormsSample.Droid
{
public class ImageFile : IImageFile
{
public void SaveImage(string name, byte[] data, string location = "temp")
{
var documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
documentsPath = System.IO.Path.Combine(documentsPath, "Orders", location);
Directory.CreateDirectory(documentsPath);
string filePath = System.IO.Path.Combine(documentsPath, name);
using (FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate))
{
int length = data.Length;
fs.Write(data, 0, length);
}
}
}
}
Finally, resolving platform implementations with the DependencyService
private void btn1_Clicked(object sender, EventArgs e)
{
var ImageUrl = "ftp://xxxxxxxxx.jpg";
//Download Image
byte[] ImgByte1 = WebClient.DownloadData(ImageUrl);
DependencyService.Get<IImageFile>().SaveImage("ImageName.jpg", ImgByte1, "imagesFolder");
}
you need to add permission WRITE_EXTERNAL_STORAGE and READ_EXTERNAL_STORAGE in AndroidMainfeast.xml, then you also need to Runtime Permission Checks in Android 6.0. Using the following code in Mainactivity.cs to request permissions.
private void checkpermission()
{
if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.WriteExternalStorage) == (int)Permission.Granted)
{
// We have permission, go ahead and use the writeexternalstorage.
}
else
{
// writeexternalstorage permission is not granted. If necessary display rationale & request.
}
if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadExternalStorage) == (int)Permission.Granted)
{
// We have permission, go ahead and use the ReadExternalStorage.
}
else
{
// ReadExternalStorage permission is not granted. If necessary display rationale & request.
}
}
Detailed info about IOS platform, you can take a look:
How to download image and save it in local storage using Xamarin-Forms.?
Update:
If you want to add text watermark on image, you need to convert byte[] to bitmap to add text firstly.
Android.Graphics.Bitmap mutableBitmap;
Android.Graphics.Bitmap bitmap;
public void ConvertImage(byte[] imageArray)
{
bitmap = BitmapFactory.DecodeByteArray(imageArray, 0, imageArray.Length);
}
public void Watermark()
{
mutableBitmap = bitmap.Copy(Android.Graphics.Bitmap.Config.Argb8888, true);
Canvas canvas = new Canvas(mutableBitmap);
// Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
paint.Color = Android.Graphics.Color.Black;
paint.TextSize = 50;
canvas.DrawText("hellod world", 50, 50, paint);
}
Then convert bitmap into byte[] to save image into local storage.
public void convertbitmap(Bitmap bitmap)
{
bitmap = mutableBitmap;
MemoryStream stream = new MemoryStream();
bitmap.Compress(Bitmap.CompressFormat.Png, 0, stream);
byte[] bitmapData = stream.ToArray();
}

How to pass xml file inside of code instead of a folder path?

I'am trying to not create a file, and pass xml document straight to a SkiaSharp method Load. I mean, is there the way to imitate a path? So here is the code:
public IActionResult svgToPng(string itemId, string mode = "
{
var svgSrc = new XmlDocument();
svgSrc.LoadXml(/*Some xml code*/);
string svgSaveAs = "save file path";
var quality = 100;
var svg = new SkiaSharp.Extended.Svg.SKSvg();
var pict = svg.Load(svgSrc); // HERE, it needs to be a path, not XmlDocument, but i want to pass straight
var dimen = new SkiaSharp.SKSizeI
(
(int) Math.Ceiling(pict.CullRect.Width),
(int) Math.Ceiling(pict.CullRect.Height)
);
var matrix = SKMatrix.MakeScale(1, 1);
var img = SKImage.FromPicture(pict, dimen, matrix);
// Convert to PNG
var skdata = img.Encode(SkiaSharp.SKEncodedImageFormat.Png, quality);
using(var stream = System.IO.File.OpenWrite(svgSaveAs))
{
skdata.SaveTo(stream);
}
ViewData["Content"] = "PNG file was created out of SVG.";
return View();
}
The Load method seems to be this:
public SKPicture Load(
using (var stream = File.OpenRead(filename))
{
return Load(stream);
}
}
look at the code of that library :
https://github.com/mono/SkiaSharp.Extended/blob/master/SkiaSharp.Extended.Svg/source/SkiaSharp.Extended.Svg.Shared/SKSvg.cs
Look at the Load method, it has multiple implementations :
public SKPicture Load(string filename)
{
using (var stream = File.OpenRead(filename))
{
return Load(stream);
}
}
public SKPicture Load(Stream stream)
{
using (var reader = XmlReader.Create(stream, xmlReaderSettings, CreateSvgXmlContext()))
{
return Load(reader);
}
}
public SKPicture Load(XmlReader reader)
{
return Load(XDocument.Load(reader));
}
You will need to pick one of them and use it. Now, nothing stops you from getting the code and adding one extra Load for an XML string for example, but since this is a library you do not control, I'd stick to what you are given.
You could use the XmlReader version, that's probably the closest one to what you want.

Error while trying to convert a .tif file to jpg: A generic error occurred in GDI+

I'm trying to convert a .tif file to .jpg but I get the following exception:
System.Runtime.InteropServices.ExternalException: A generic error occurred in GDI+.
I'm running this on a Windows 10 machine.
How can I fix this error?
This is my program:
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using Encoder = System.Drawing.Imaging.Encoder;
namespace Test
{
class Program
{
static void Main(string[] args)
{
SaveAsJpeg(#"C:\Temp\Target\test_converted.jpg", 70);
if (!System.Diagnostics.Debugger.IsAttached) return;
Console.WriteLine("Press Enter to quit.");
Console.ReadLine();
}
public static FileInfo SaveAsJpeg(string newFilePath, long quality)
{
// File to convert
var path = #"C:\Temp\test.tif";
using (var stream = new MemoryStream(File.ReadAllBytes(path)))
{
using (var imageToConvert = Image.FromStream(stream))
{
// This line throws the error
imageToConvert.Save(newFilePath, GetDecoder(ImageFormat.Jpeg), GetQualityEncoderParameters(quality));
}
}
return new FileInfo(newFilePath);
}
private static ImageCodecInfo GetDecoder(ImageFormat format)
{
var codecs = ImageCodecInfo.GetImageDecoders();
return codecs.FirstOrDefault(codec => codec.FormatID == format.Guid);
}
private static EncoderParameters GetQualityEncoderParameters(long quality)
{
var qualityParameter = new EncoderParameter(Encoder.Quality, quality);
var encoderParameters = new EncoderParameters(1) { Param = { [0] = qualityParameter } };
return encoderParameters;
}
}
}
So far I have...
Made sure that C:\Temp\Target exists
Made sure that Everyone have full read/write access to C:\Temp\Target
These are the file properties:
Edit:
After trying the same code with a different image, and no errors were thrown, there's probably an issue with the image i'm trying to convert. An image with the following properties is successfully converted:
I don't know how, but this seems to do the trick. No errors are thrown, and the image is converted successfully:
public static FileInfo SaveAsJpeg(string newFilePath, long quality)
{
var path = #"C:\Temp\test.tif";
using (var stream = new MemoryStream(File.ReadAllBytes(path)))
{
var imageToConvert = Image.FromStream(stream);
using (imageToConvert = new Bitmap(imageToConvert))
{
imageToConvert.Save(newFilePath, GetDecoder(ImageFormat.Jpeg), GetQualityEncoderParameters(quality));
}
}
return new FileInfo(newFilePath);
}
If anyone could elaborate on why this works, that would be great.

How to read back JPG with multiple data created in Unity? ( C# )

I'm really sorry if this has been asked before, or if it is obvious, but I'm not entirely sure how to progress here. Please excuse the length of the question.
For a research project, my intention is to create a JPG image that contains after the JPG data a small plain text and another image as well. This image would be used later as an AR marker that 'contains' all the data to be displayed. I read that this could also be achieved with Stenography, but encrypting is not necessarily the goal, I think.
So far, using various other methods found here, the script can create a readable image that adds the byte[] data of the other content right after the first JPG, but I'm not entirely sure how to read it back.
using UnityEngine;
using Crosstales.FB;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System;
using System.Text;
public class JPGMan : MonoBehaviour {
public UnityEngine.UI.Image markerThumb;
public UnityEngine.UI.Image mediaThumb;
public UnityEngine.UI.InputField descF;
Encoding u8 = Encoding.UTF8;
public void initLoadMarker()
{
string markerExt = "";
string markerPath = FileBrowser.OpenSingleFile("Image for Marker", "", markerExt);
StartCoroutine(loadMarker(markerPath));
Debug.Log(markerPath);
}
public void initLoadMedia()
{
string mediaExt = "";
string mediaPath = FileBrowser.OpenSingleFile("Image for Media", "", mediaExt);
StartCoroutine(loadMedia(mediaPath));
Debug.Log(mediaPath);
}
IEnumerator loadMarker(string mPath)
{
WWW markerWWW;
using (markerWWW = new WWW("File:///" + mPath))
{
yield return markerWWW;
Debug.Log(markerWWW.isDone);
Rect protoRect = new Rect(Vector2.zero, new Vector2(markerWWW.texture.width, markerWWW.texture.height));
Sprite protoSprite = Sprite.Create(markerWWW.texture, protoRect, Vector2.zero);
markerThumb.sprite = protoSprite;
markerThumb.preserveAspect = true;
}
}
IEnumerator loadMedia(string mPath)
{
WWW mediaWWW;
using (mediaWWW = new WWW("File:///" + mPath))
{
yield return mediaWWW;
Debug.Log(mediaWWW.isDone);
Rect protoRect = new Rect(Vector2.zero, new Vector2(mediaWWW.texture.width, mediaWWW.texture.height));
Sprite protoSprite = Sprite.Create(mediaWWW.texture, protoRect, Vector2.zero);
mediaThumb.sprite = protoSprite;
mediaThumb.preserveAspect = true;
}
}
public void encodeJPG()
{
string finalExt = "jpg";
string finalPath = FileBrowser.SaveFile("Save Marker", "", "aMARKER", finalExt);
Texture2D protoJPG = new Texture2D((int)mediaThumb.sprite.textureRect.width, (int)mediaThumb.sprite.textureRect.height);
Color[] pixels = mediaThumb.sprite.texture.GetPixels((int)mediaThumb.sprite.textureRect.x, (int)mediaThumb.sprite.textureRect.y, (int)mediaThumb.sprite.textureRect.width, (int)mediaThumb.sprite.textureRect.height);
protoJPG.SetPixels(pixels);
protoJPG.Apply();
Texture2D protoMarker = new Texture2D((int)markerThumb.sprite.textureRect.width, (int)markerThumb.sprite.textureRect.height);
Color[] markerPixels = markerThumb.sprite.texture.GetPixels((int)markerThumb.sprite.textureRect.x, (int)markerThumb.sprite.textureRect.y, (int)markerThumb.sprite.textureRect.width, (int)markerThumb.sprite.textureRect.height);
protoMarker.SetPixels(markerPixels);
protoMarker.Apply();
byte[] markerBytes = protoMarker.EncodeToJPG(100);
byte[] imgBytes = protoJPG.EncodeToJPG(100);
byte[] textBytes = System.Text.Encoding.Unicode.GetBytes(descF.text);
byte[] finalBytes = new byte[markerBytes.Length + textBytes.Length + imgBytes.Length];
System.Buffer.BlockCopy(markerBytes, 0, finalBytes, 0, markerBytes.Length);
System.Buffer.BlockCopy(textBytes, 0, finalBytes, markerBytes.Length, textBytes.Length);
System.Buffer.BlockCopy(imgBytes, 0, finalBytes, markerBytes.Length + textBytes.Length, imgBytes.Length);
File.WriteAllBytes(finalPath, finalBytes);
}
public void decodeJPG()
{
string inExt = "jpg";
string inPath = FileBrowser.OpenSingleFile("Open Marker", "", inExt);
StartCoroutine(loadMarkerImage(inPath));
}
IEnumerator loadMarkerImage(string inPath_)
{
byte[] bytesFile;
char[] lookFiles = new char[] {'J','F','I','F'};
using (FileStream fs = File.OpenRead(inPath_))
{
yield return fs;
bytesFile = new byte[20];
fs.Read(bytesFile, 0, 20);
fs.Close();
}
byte[] searchBytes = u8.GetBytes(lookFiles);
List<int> rList = IndexOfSequence(bytesFile, searchBytes, 0);
Debug.Log(rList[0].ToString());
}
I'm thinking that I could maybe find a way to write to the JPG spec's APP0 marker, but I haven't found much about doing this specifically in Unity. Another could be to loop through the whole byte[] to find the headers and thus differentiate the data, although I read that reading all the bytes in one operation is not particularly fast.
Finally, I was thinking of appending the serialization of a class that contains the extra data, making it possible to deserialize it and just refer to it as properties of the class. But that's probably a bad idea.
I am aware this is very unusual, and I don't mind as well just using a regular database and link the images to that data.
Is there anyone that could help with this matter? Any help is appreciated.
Thank you all for your time.

how to diagnose freeimage loadfromstream errors

I am using a C# FreeImage wrapper.
I am trying to open a PDF file containing images, and "extract" those images into windows Bitmap objects. I am following the guidelines described in articles on the internet, following loosely the following pattern:
byte[] bytes = GetPdfFile();
iTextSharp.text.pdf.PdfReader reader = new iTextSharp.text.pdf.PdfReader(bytes);
int pageNumber = 0;
for (int i = 0; i <= reader.XrefSize - 1; i++)
{
pdfObj = reader.GetPdfObject(i);
if ((pdfObj != null) && pdfObj.IsStream())
{
pdfStream = (iTextSharp.text.pdf.PdfStream)pdfObj;
iTextSharp.text.pdf.PdfObject subtype = pdfStream.Get(iTextSharp.text.pdf.PdfName.SUBTYPE);
if ((subtype != null) && subtype.ToString().Equals(iTextSharp.text.pdf.PdfName.IMAGE.ToString()))
{
pageNumber++;
byte[] imgBytes = iTextSharp.text.pdf.PdfReader.GetStreamBytesRaw((iTextSharp.text.pdf.PRStream)pdfStream);
if ((imgBytes != null))
{
// in my case images are in TIF Group 4 format
using (MemoryStream ms = new MemoryStream(imgBytes))
{
FreeImageAPI.FIBITMAP bmp = FreeImageAPI.FreeImage.LoadFromStream(ms);
// in my case bmp with IsNull = true is returned
if (!bmp.IsNull)
{
using (MemoryStream msOut = new MemoryStream())
{
FreeImageAPI.FreeImage.SaveToStream(bmp, msOut, ... ); // etc.
}
}
}
}
}
}
}
Does anybody have a suggestion on how to troubleshoot this, given that no exception is returned - some kind of GetLastError FreeImage function?
Thank you
Although it doesn't appear that the SetOutputMessage function was added to the .NET wrapper library, it is possible to call it directly from the FreeImage library.
That is, adding the function to your code using:
[DllImport("FreeImage", EntryPoint = "FreeImage_SetOutputMessage")]
internal static extern void SetOutputMessage(OutputMessageFunction outputMessageFunction);
The following code shows an Exception being thrown from the Save function, rather than returning false (with the following console output):
Test 1... Success
Test 2...
Unhandled Exception: System.Exception: only 24-bit highcolor or 8-bit greyscale/palette bitmaps can be saved as JPEG
at ConsoleApplication1.Program.OutputMessage(FREE_IMAGE_FORMAT format, String message) ...\ConsoleApplication1\Program.cs:line 35
at FreeImageAPI.FreeImage.Save(FREE_IMAGE_FORMAT fif, FIBITMAP dib, String filename, FREE_IMAGE_SAVE_FLAGS flags)
at ConsoleApplication1.Program.Main(String[] args) in ...\ConsoleApplication1\Program.cs:line 28
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using FreeImageAPI;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{ FIBITMAP bitmap;
bool success;
SetOutputMessage(OutputMessage);
Console.Write("Test 1... ");
bitmap = FreeImage.CreateFromBitmap(new Bitmap(320, 240, PixelFormat.Format24bppRgb));
success = FreeImage.Save(FREE_IMAGE_FORMAT.FIF_JPEG, bitmap, "output.jpg", FREE_IMAGE_SAVE_FLAGS.JPEG_QUALITYNORMAL);
FreeImage.Unload(bitmap);
Console.WriteLine("Success");
Console.Write("Test 2... ");
bitmap = FreeImage.CreateFromBitmap(new Bitmap(320, 240));
success = FreeImage.Save(FREE_IMAGE_FORMAT.FIF_JPEG, bitmap, "output.jpg", FREE_IMAGE_SAVE_FLAGS.JPEG_QUALITYNORMAL);
FreeImage.Unload(bitmap);
Console.WriteLine("Success");
}
static void OutputMessage(FREE_IMAGE_FORMAT format, string message)
{ throw new Exception(message);
}
[DllImport("FreeImage", EntryPoint = "FreeImage_SetOutputMessage")]
internal static extern void SetOutputMessage(OutputMessageFunction outputMessageFunction);
}
}

Categories