Created
June 14, 2016 07:00
-
-
Save elliotwoods/2e1228201451d63d1765135da44c2782 to your computer and use it in GitHub Desktop.
FreeImage version of FileTexture for DX11
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using VVVV.PluginInterfaces.V2; | |
using VVVV.DX11; | |
using FeralTic.DX11.Resources; | |
using VVVV.PluginInterfaces.V1; | |
using System.ComponentModel.Composition; | |
using FeralTic.DX11; | |
using System.Runtime.InteropServices; | |
using System.Diagnostics; | |
using System.Threading; | |
using FreeImageAPI; | |
using System.IO; | |
using SlimDX.DXGI; | |
namespace VVVV.Nodes.Ximea | |
{ | |
#region PluginInfo | |
[PluginInfo(Name = "FileTexture", | |
Category = "DX11", | |
Version = "FreeImage", | |
Help = "Load FreeImage files", | |
Tags = "")] | |
#endregion PluginInfo | |
public class VideoInDX11Node : IPluginEvaluate, IDisposable, IDX11ResourceProvider | |
{ | |
#region fields & pins | |
IPluginHost FHost; | |
[Input("Filename", StringType = StringType.Filename)] | |
IDiffSpread<string> FInFilename; | |
[Input("Reload", IsBang = true)] | |
ISpread<bool> FInReload; | |
[Output("Texture Out", Order = 0)] | |
protected Pin<DX11Resource<DX11DynamicTexture2D>> FTextureOut; | |
[Output("Size")] | |
ISpread<int> FOutSize; | |
[Output("Format")] | |
ISpread<SlimDX.DXGI.Format> FOutFormat; | |
[Output("Valid")] | |
ISpread<bool> FOutValid; | |
[Output("Status")] | |
ISpread<string> FOutStatus; | |
Dictionary<string, Image> FLoadedImages = new Dictionary<string, Image>(); | |
Dictionary<int, string> FLoadedTextureFiles = new Dictionary<int, string>(); | |
#endregion fields & pins | |
[ImportingConstructor()] | |
public VideoInDX11Node(IPluginHost Host) | |
{ | |
this.FHost = Host; | |
} | |
class Image | |
{ | |
public System.Type Type; | |
public uint channelCount; | |
public byte[] data; | |
public ulong width; | |
public ulong pitch; | |
public ulong height; | |
public ulong size; | |
public Format format; | |
} | |
bool FFirstRun = true; | |
public void Evaluate(int SpreadMax) | |
{ | |
if(FFirstRun) | |
{ | |
this.FTextureOut.SliceCount = 0; | |
FFirstRun = false; | |
} | |
//resize output | |
while (this.FTextureOut.SliceCount > SpreadMax) | |
{ | |
var resource = FTextureOut[FTextureOut.SliceCount - 1]; | |
if (resource != null) | |
{ | |
resource.Dispose(); | |
resource = null; | |
} | |
} | |
while (this.FTextureOut.SliceCount < SpreadMax) | |
{ | |
this.FTextureOut.Add(new DX11Resource<DX11DynamicTexture2D>()); | |
} | |
this.FTextureOut.SliceCount = SpreadMax; | |
//unload anything with a reload flat | |
for(int i=0; i<SpreadMax; i++) | |
{ | |
if(FInReload[i]) | |
{ | |
var filename = FInFilename[i]; | |
if(FLoadedImages.ContainsKey(filename)) | |
{ | |
//unload the texture | |
FLoadedImages.Remove(filename); | |
//remove texture from loaded texture flags | |
var keys = new List<int>(FLoadedTextureFiles.Keys); | |
foreach(var key in keys) | |
{ | |
if(FLoadedTextureFiles[key] == filename) | |
{ | |
FLoadedTextureFiles.Remove(key); | |
} | |
} | |
} | |
} | |
} | |
FOutSize.SliceCount = SpreadMax * 2; | |
FOutFormat.SliceCount = SpreadMax; | |
FOutStatus.SliceCount = SpreadMax; | |
FOutValid.SliceCount = SpreadMax; | |
for (int i = 0; i < SpreadMax; i++) | |
{ | |
try | |
{ | |
var filename = FInFilename[i]; | |
if (!File.Exists(filename)) | |
{ | |
throw (new Exception("File not found")); | |
} | |
if (!FLoadedImages.ContainsKey(filename)) | |
{ | |
FLoadedImages.Add(filename, LoadImage(filename)); | |
} | |
var image = FLoadedImages[filename]; | |
FOutSize[i * 2 + 0] = (int) image.width; | |
FOutSize[i * 2 + 1] = (int)image.height; | |
FOutFormat[i] = image.format; | |
FOutValid[i] = true; | |
FOutStatus[i] = "OK"; | |
} | |
catch (Exception e) | |
{ | |
FOutSize[i * 2 + 0] = 0; | |
FOutSize[i * 2 + 1] = 0; | |
FOutValid[i] = false; | |
FOutStatus[i] = e.Message; | |
} | |
} | |
//unload any unused images | |
{ | |
var filenames = new List<string>(FLoadedImages.Keys); | |
foreach(var filename in filenames) | |
{ | |
//double check we didn't already remove | |
if(!FInFilename.Contains(filename) && FLoadedImages.ContainsKey(filename)) | |
{ | |
FLoadedImages.Remove(filename); | |
} | |
} | |
} | |
} | |
Image LoadImage(string filename) | |
{ | |
FREE_IMAGE_FORMAT format = FreeImage.GetFileType(filename, 0); | |
var bmp = FreeImage.Load(format, filename, FREE_IMAGE_LOAD_FLAGS.JPEG_ACCURATE); | |
try | |
{ | |
if (bmp.IsNull == true) | |
{ | |
throw (new Exception("Couldn't load file")); | |
} | |
//we need some conversion from strange palettes | |
if (FreeImage.GetColorType(bmp) == FREE_IMAGE_COLOR_TYPE.FIC_PALETTE || FreeImage.GetBPP(bmp) < 8) | |
{ | |
FIBITMAP converted; | |
if (FreeImage.IsTransparent(bmp)) | |
converted = FreeImage.ConvertTo32Bits(bmp); | |
else if (FreeImage.IsGreyscaleImage(bmp)) | |
converted = FreeImage.ConvertTo8Bits(bmp); | |
else | |
converted = FreeImage.ConvertTo24Bits(bmp); | |
FreeImage.Unload(bmp); | |
bmp = converted; | |
} | |
//now we should have a fairly sensible 8, 24 or 32bit (uchar) image | |
//or a float / hdr image | |
uint width = FreeImage.GetWidth(bmp); | |
uint height = FreeImage.GetHeight(bmp); | |
uint bpp = FreeImage.GetBPP(bmp); | |
uint pitch = FreeImage.GetPitch(bmp); | |
FREE_IMAGE_TYPE type = FreeImage.GetImageType(bmp); | |
var dataType = typeof(byte); | |
uint channelCount = 1; | |
Format DX11Format = Format.Unknown; | |
if (type == FREE_IMAGE_TYPE.FIT_BITMAP) | |
{ | |
//standard image (8bbp) | |
dataType = typeof(byte); | |
channelCount = bpp / 8; | |
switch(channelCount) | |
{ | |
case 1: | |
DX11Format = Format.R8_UNorm; | |
break; | |
case 2: | |
DX11Format = Format.R8G8_UNorm; | |
break; | |
case 4: | |
DX11Format = Format.R8G8B8A8_UNorm; | |
break; | |
default: | |
throw (new Exception("Channel count is not supported : " + channelCount.ToString())); | |
} | |
} | |
else | |
{ | |
switch (type) | |
{ | |
case (FREE_IMAGE_TYPE.FIT_UINT16): | |
dataType = typeof(UInt16); | |
channelCount = 1; | |
DX11Format = Format.R16_UNorm; | |
break; | |
case (FREE_IMAGE_TYPE.FIT_INT16): | |
dataType = typeof(Int16); | |
channelCount = 1; | |
DX11Format = Format.R16_SNorm; | |
break; | |
case (FREE_IMAGE_TYPE.FIT_UINT32): | |
dataType = typeof(UInt32); | |
channelCount = 1; | |
DX11Format = Format.R32_UInt; | |
break; | |
case (FREE_IMAGE_TYPE.FIT_INT32): | |
dataType = typeof(Int32); | |
channelCount = 1; | |
DX11Format = Format.R32_SInt; | |
break; | |
case (FREE_IMAGE_TYPE.FIT_FLOAT): | |
dataType = typeof(Single); | |
channelCount = 1; | |
DX11Format = Format.R32_Float; | |
break; | |
case (FREE_IMAGE_TYPE.FIT_RGBF): | |
dataType = typeof(Single); | |
channelCount = 3; | |
DX11Format = Format.R32G32B32_Float; | |
break; | |
case (FREE_IMAGE_TYPE.FIT_RGBAF): | |
dataType = typeof(Single); | |
channelCount = 4; | |
DX11Format = Format.R32G32B32A32_Float; | |
break; | |
default: | |
throw (new Exception("Image of unknown format : " + type.ToString())); | |
} | |
} | |
//flip the image vertically | |
FreeImage.FlipVertical(bmp); | |
//load the data out | |
var size = (ulong)Marshal.SizeOf(dataType) * width * height * channelCount; | |
var data = new byte[size]; | |
Marshal.Copy(FreeImage.GetBits(bmp), data, 0, (int)size); | |
//unload the bmp | |
FreeImage.Unload(bmp); | |
//keep image in memory | |
var newImage = new Image | |
{ | |
Type = dataType, | |
channelCount = channelCount, | |
width = width, | |
pitch = pitch, | |
height = height, | |
size = size, | |
format = DX11Format, | |
data = data | |
}; | |
return newImage; | |
} | |
catch (Exception e) | |
{ | |
if (!bmp.IsNull) | |
{ | |
FreeImage.Unload(bmp); | |
} | |
throw (e); | |
} | |
} | |
public void Dispose() | |
{ | |
foreach(var textureSlice in FTextureOut) | |
{ | |
if(textureSlice != null) | |
{ | |
textureSlice.Dispose(); | |
} | |
} | |
} | |
public void Update(DX11RenderContext context) | |
{ | |
for(int i=0; i<FTextureOut.SliceCount; i++) | |
{ | |
if(i > FInFilename.SliceCount) | |
{ | |
continue; | |
} | |
var filename = FInFilename[i]; | |
if(!FLoadedImages.ContainsKey(filename)) | |
{ | |
continue; | |
} | |
var image = FLoadedImages[filename]; | |
var textureSlice = FTextureOut[i]; | |
DX11DynamicTexture2D texture; | |
bool needsTexture = false; | |
bool needsUpdate = false; | |
if(textureSlice.Contains(context)) | |
{ | |
if (textureSlice[context].Width != (int)image.width | |
|| textureSlice[context].Height != (int)image.height | |
|| textureSlice[context].Format != image.format) | |
{ | |
textureSlice[context].Dispose(); | |
needsTexture = true; | |
} | |
} else | |
{ | |
needsTexture = true; | |
} | |
if(needsTexture) | |
{ | |
texture = new DX11DynamicTexture2D(context, (int)image.width, (int)image.height, image.format); | |
textureSlice[context] = texture; | |
needsUpdate = true; | |
} else | |
{ | |
texture = textureSlice[context]; | |
} | |
if (FLoadedTextureFiles.ContainsKey(i)) | |
{ | |
needsUpdate |= FLoadedTextureFiles[i] != filename; | |
} else | |
{ | |
needsUpdate = true; | |
} | |
if(needsUpdate) | |
{ | |
texture.WriteData(image.data); | |
FLoadedTextureFiles[i] = filename; | |
} | |
} | |
} | |
public void Destroy(DX11RenderContext context, bool force) | |
{ | |
foreach(var textureSlice in FTextureOut) | |
{ | |
if(textureSlice != null) | |
{ | |
if(textureSlice.Contains(context)) | |
{ | |
textureSlice[context].Dispose(); | |
} | |
} | |
} | |
} | |
public void Update(IPluginIO pin, DX11RenderContext context) | |
{ | |
this.Update(context); | |
} | |
public void Destroy(IPluginIO pin, DX11RenderContext context, bool force) | |
{ | |
this.Destroy(context, force); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment