/// <summary> /// Expands images to have an alpha channel and swaps red and blue channels /// </summary> /// <param name="fBitmap">Image to process</param> /// <param name="imageType">Type of the image for the procedure</param> /// <returns></returns> private static FIBITMAP ConvertAndSwapChannels(FIBITMAP fBitmap, FREE_IMAGE_TYPE imageType) { FIBITMAP bgra; switch (imageType) { //Bitmap files are expanded to 32 bits before switching channels case FREE_IMAGE_TYPE.FIT_BITMAP: bgra = FreeImage.ConvertTo32Bits(fBitmap); FreeImage.UnloadEx(ref fBitmap); fBitmap = bgra; SwitchRedAndBlueChannels(fBitmap); break; //RGBF are switched before adding an alpha channel. case FREE_IMAGE_TYPE.FIT_RGBF: { // Swap R and B channels to make it BGR, then add an alpha channel SwitchRedAndBlueChannels(fBitmap); bgra = FreeImage.ConvertToType(fBitmap, FREE_IMAGE_TYPE.FIT_RGBAF, true); FreeImage.UnloadEx(ref fBitmap); fBitmap = bgra; } break; case FREE_IMAGE_TYPE.FIT_RGBAF: { // Swap R and B channels to make it BGRA SwitchRedAndBlueChannels(fBitmap); } break; } return(fBitmap); }
public override object Process(FileInfo myInfo, string FilePath) { TextureComponent myTexture = new TextureComponent(); FileStream myStream = ReadFileStream(myInfo, FilePath); var Result2 = FreeImage.LoadFromStream(myStream); myTexture.Width = FreeImage.GetWidth(Result2); myTexture.Height = FreeImage.GetHeight(Result2); var Type = FreeImage.GetImageType(Result2); Debug.WriteLine("ImageType: " + Enum.GetName(typeof(FREE_IMAGE_TYPE), Type) + " was detected!"); FreeImage.ConvertToType(Result2, FREE_IMAGE_TYPE.FIT_BITMAP, true); // The bits per pixel and image type may have changed myTexture.bpp = FreeImage.GetBPP(Result2); myTexture.pitch = (int)FreeImage.GetPitch(Result2); myTexture.redMask = FreeImage.GetRedMask(Result2); myTexture.greenMask = FreeImage.GetGreenMask(Result2); myTexture.blueMask = FreeImage.GetBlueMask(Result2); myTexture.ImageBytes = new uint[(int)(myTexture.Width * myTexture.Height)]; // Create the byte array for the data var Data = new byte[(int)((myTexture.Width * myTexture.Height * myTexture.bpp - 1f) / 8) + 1]; //Converts the pixel data to bytes, do not try to use this call to switch the color channels because that only works for 16bpp bitmaps FreeImage.ConvertToRawBits(Data, Result2, myTexture.pitch, myTexture.bpp, myTexture.redMask, myTexture.greenMask, myTexture.blueMask, true); int Count = Vector <byte> .Count; float Iterator = Data.Length - (Data.Length % Count); Vector <byte> Work; int DestIterator = 0; for (int i = 0; i < Iterator; i += Count) { Work = new Vector <byte>(Data, i); var Result = Vector.AsVectorUInt32(Work); Result.CopyTo(myTexture.ImageBytes, DestIterator++); } //Array.Copy(Data, 0, myTexture.ImageBytes, 0, Data.Length); //FreeImage.FreeHbitmap(Result2); //TODO: FREE THE IMAGE OR LEAK myStream.Close(); return(myTexture); }
/// <summary> /// Expands images to have an alpha channel and swaps red and blue channels /// </summary> /// <param name="fBitmap">Image to process</param> /// <param name="imageType">Type of the image for the procedure</param> /// <returns></returns> private static FIBITMAP ConvertAndSwapChannels(FIBITMAP fBitmap, FREE_IMAGE_TYPE imageType) { FIBITMAP bgra; switch (imageType) { // Return BGRA images as is case FREE_IMAGE_TYPE.FIT_RGBAF: case FREE_IMAGE_TYPE.FIT_RGBA16: break; // Add an alpha channel to BGRA images without one case FREE_IMAGE_TYPE.FIT_RGBF: bgra = FreeImage.ConvertToType(fBitmap, FREE_IMAGE_TYPE.FIT_RGBAF, true); FreeImage.UnloadEx(ref fBitmap); fBitmap = bgra; break; case FREE_IMAGE_TYPE.FIT_RGB16: bgra = FreeImage.ConvertToType(fBitmap, FREE_IMAGE_TYPE.FIT_RGBA16, true); FreeImage.UnloadEx(ref fBitmap); fBitmap = bgra; break; // Add an alpha channel to RGB images // Swap the red and blue channels of RGBA images default: // Bitmap and other formats are converted to 32-bit by default bgra = FreeImage.ConvertTo32Bits(fBitmap); SwitchRedAndBlueChannels(bgra); FreeImage.UnloadEx(ref fBitmap); fBitmap = bgra; break; } return(fBitmap); }
/// <summary> /// Expands images to have an alpha channel and swaps red and blue channels /// </summary> /// <param name="fBitmap">Image to process</param> /// <param name="imageType">Type of the image for the procedure</param> /// <returns></returns> private static FIBITMAP ConvertAndSwapChannels(FIBITMAP fBitmap, FREE_IMAGE_TYPE imageType) { FIBITMAP bgra; switch (imageType) { // RGBF are switched before adding an alpha channel. case FREE_IMAGE_TYPE.FIT_RGBF: // Swap R and B channels to make it BGR, then add an alpha channel SwitchRedAndBlueChannels(fBitmap); bgra = FreeImage.ConvertToType(fBitmap, FREE_IMAGE_TYPE.FIT_RGBAF, true); FreeImage.UnloadEx(ref fBitmap); fBitmap = bgra; break; case FREE_IMAGE_TYPE.FIT_RGB16: // Swap R and B channels to make it BGR, then add an alpha channel SwitchRedAndBlueChannels(fBitmap); bgra = FreeImage.ConvertToType(fBitmap, FREE_IMAGE_TYPE.FIT_RGBA16, true); FreeImage.UnloadEx(ref fBitmap); fBitmap = bgra; break; case FREE_IMAGE_TYPE.FIT_RGBAF: case FREE_IMAGE_TYPE.FIT_RGBA16: // Swap R and B channels to make it BGRA SwitchRedAndBlueChannels(fBitmap); break; default: // Bitmap and other formats are converted to 32-bit by default bgra = FreeImage.ConvertTo32Bits(fBitmap); SwitchRedAndBlueChannels(bgra); FreeImage.UnloadEx(ref fBitmap); fBitmap = bgra; break; } return(fBitmap); }
public void ConvertBitmap(ImageType newImageType) { if (ImageType == newImageType) { return; } int newbpp, oldbpp; FREE_IMAGE_TYPE newType = ImageTypeToFreeImageType(newImageType, out newbpp); FREE_IMAGE_TYPE oldType = ImageTypeToFreeImageType(ImageType, out oldbpp); if (newType != oldType || newbpp != oldbpp) { FIBITMAP newBitmap = FreeImage.ConvertToType(_bitmap, newType, false); FreeImage.UnloadEx(ref _bitmap); _bitmap = newBitmap; } // check if a change in color space is needed. if (IsGammaCorrected(ImageType) != IsGammaCorrected(newImageType)) { double gamma; if (IsGammaCorrected(ImageType)) // from linear color space to non linear { gamma = 1d / 2.2d; } else { gamma = 2.2d; } if (!FreeImage.AdjustGamma(_bitmap, gamma)) { throw new ContentLoadException("FreeImage was unable to adjust the bitmap's gamma"); } } ImageType = newImageType; }
private void SaveHelper(TextureFormat format, byte[] rawBytes, int texwidth, int texheight, bool convertTo16, ChannelsPerMap exportChannels, string imagePath, FREE_IMAGE_FORMAT destFormat, FREE_IMAGE_SAVE_FLAGS saveFlags) { int bytesPerPixel = 4; FREE_IMAGE_TYPE imageType = FREE_IMAGE_TYPE.FIT_BITMAP; switch (format) { case TextureFormat.RGBAHalf: imageType = FREE_IMAGE_TYPE.FIT_RGBA16; bytesPerPixel = 8; break; case TextureFormat.RGBAFloat: imageType = FREE_IMAGE_TYPE.FIT_RGBAF; bytesPerPixel = 16; break; case TextureFormat.ARGB32: imageType = FREE_IMAGE_TYPE.FIT_BITMAP; bytesPerPixel = 4; //tex.GetPixels32(); //ConvertBGRAtoARGBScanline(dib); // convert back to ARGB if (FreeImage.IsLittleEndian()) { for (int j = 0; j < rawBytes.Length; j += 4) { // convert BGRA to ARGB var a = rawBytes[j]; var r = rawBytes[j + 1]; rawBytes[j] = rawBytes[j + 3]; rawBytes[j + 1] = rawBytes[j + 2]; rawBytes[j + 2] = r; rawBytes[j + 3] = a; } } break; case TextureFormat.RGB24: imageType = FREE_IMAGE_TYPE.FIT_BITMAP; bytesPerPixel = 3; if (FreeImage.IsLittleEndian()) { // convert back to RGB for (int i = 0; i < rawBytes.Length; i += 3) { // convert BGR to RGB var r = rawBytes[i]; rawBytes[i] = rawBytes[i + 2]; rawBytes[i + 2] = r; } } break; } FIBITMAP dib = FreeImage.ConvertFromRawBits(rawBytes, imageType, texwidth, texheight, texwidth * bytesPerPixel, (uint)bytesPerPixel * 8, 0, 0, 0, false); if (dib.IsNull) { Debug.LogError("Dib is NULL!!!"); } rawBytes = null; GC.Collect(); if (convertTo16) { dib = FreeImage.ConvertToType(dib, FREE_IMAGE_TYPE.FIT_RGBA16, false); format = TextureFormat.RGBAHalf; } switch (exportChannels) { case ChannelsPerMap.RGB: // remove alpha channel switch (format) { case TextureFormat.RGBAFloat: dib = FreeImage.ConvertToRGBF(dib); break; case TextureFormat.RGBAHalf: dib = FreeImage.ConvertToType(dib, FREE_IMAGE_TYPE.FIT_RGB16, false); break; case TextureFormat.ARGB32: dib = FreeImage.ConvertTo24Bits(dib); break; } break; case ChannelsPerMap.R: dib = FreeImage.GetChannel(dib, FREE_IMAGE_COLOR_CHANNEL.FICC_RED); break; // if already RGBA don't need to do any conversion default: break; } try { using (FileStream saveStream = new FileStream(imagePath, FileMode.OpenOrCreate, FileAccess.Write)) { Debug.Log("FreeImage::FileSaveSuccess: " + imagePath + " :" + FreeImage.SaveToStream(ref dib, saveStream, destFormat, saveFlags, true)); } } catch (Exception e) { Debug.LogException(e); //progressBar.DoneProgress(); FreeImage.UnloadEx(ref dib); throw; } //if (progressBar != null) //{ // UnityThreadHelper.Dispatcher.Dispatch(() => // { // progressBar.Increment(); // }); //} }
public RenderTexture LoadImage(string path, bool isLinear = false, bool isGrayscale = false, bool doMipMaps = false, bool forceGC = false, bool premultiplyAlpha = false) { // default bits per channel //uint origBPP = outBPP = 0; bool successfullyLoadedRaw = false; int width = 0, height = 0; TextureFormat formatToLoad = TextureFormat.ARGB32; RenderTextureFormat rtFormat = RenderTextureFormat.ARGB32; bool forceGrayscaleAfterTexture2D = false; //System.Threading.Thread newThread = new System.Threading.Thread(() => { var loadType = System.IO.Path.GetExtension(path); FREE_IMAGE_LOAD_FLAGS loadFlags = FREE_IMAGE_LOAD_FLAGS.DEFAULT; switch (loadType) { case ".png": loadFlags = FREE_IMAGE_LOAD_FLAGS.PNG_IGNOREGAMMA; break; case ".jpg": case ".jpeg": loadFlags = FREE_IMAGE_LOAD_FLAGS.JPEG_ACCURATE; break; } // Format is stored in 'format' on successfull load. FREE_IMAGE_FORMAT format = FREE_IMAGE_FORMAT.FIF_UNKNOWN; FIBITMAP dib; bool isModifiedEXR = false; char yChar = 'Y'; byte yByte = Convert.ToByte(yChar); char rChar = 'R'; byte rByte = Convert.ToByte(rChar); //byte[] byteArray = File.ReadAllBytes(path); FileStream stream = null; if (Path.GetExtension(path).ToLower() == ".exr") { stream = new FileStream(path, FileMode.Open); stream.Position = 66; isModifiedEXR = (stream.ReadByte() == rByte); if (isModifiedEXR) { Debug.Log("<color=blue>*** This is a modified EXR </color>"); //byteArray[66] = yByte; stream.Position = 66; stream.WriteByte(yByte); stream.Position = 0; } } #if UNITY_STANDALONE_OSX if (stream == null) { stream = new FileStream(path, FileMode.Open); } dib = FreeImage.LoadFromStream(stream, loadFlags, ref format); #else dib = FreeImage.LoadEx(path, loadFlags, ref format); Debug.Log("Used Heap Size After FreeImage.LoadEx: " + Profiler.GetMonoUsedSizeLong() / 1024 / 1024); #endif if (stream != null) { stream.Dispose(); GC.Collect(); Debug.Log("Used Heap Size After stream.Dispose: " + Profiler.GetMonoUsedSizeLong() / 1024 / 1024); } if (isModifiedEXR) { using (FileStream fs = new FileStream(path, FileMode.Open)) { fs.Position = 66; fs.WriteByte(rByte); fs.Position = 0; } } rtFormat = RenderTextureFormat.ARGB32; try { // Error handling if (dib.IsNull) { return(null); } FREE_IMAGE_TYPE origInputType = FreeImage.GetImageType(dib); //Debug.Log("DIB for :" + path); // read bits per channel of loaded image uint origBPP = FreeImage.GetBPP(dib); var header = FreeImage.GetInfoHeaderEx(dib); //Debug.Log("original BPP:" + origBPP); //Debug.Log("origInputType:" + origInputType); // check here if we need to convert single channel textures to RGB or vice versa based on source input texture type and destination type expected FREE_IMAGE_TYPE destType = FREE_IMAGE_TYPE.FIT_UNKNOWN; switch (origInputType) { case FREE_IMAGE_TYPE.FIT_UINT16: if (!isGrayscale) { destType = FREE_IMAGE_TYPE.FIT_RGBAF; } else { destType = FREE_IMAGE_TYPE.FIT_FLOAT; } break; case FREE_IMAGE_TYPE.FIT_RGBF: case FREE_IMAGE_TYPE.FIT_RGBAF: destType = isGrayscale ? FREE_IMAGE_TYPE.FIT_FLOAT : FREE_IMAGE_TYPE.FIT_RGBAF; break; case FREE_IMAGE_TYPE.FIT_RGB16: case FREE_IMAGE_TYPE.FIT_RGBA16: destType = isGrayscale ? FREE_IMAGE_TYPE.FIT_FLOAT : FREE_IMAGE_TYPE.FIT_RGBAF; break; case FREE_IMAGE_TYPE.FIT_BITMAP: if (isGrayscale) { if (Mathf.IsPowerOfTwo(header.biWidth) && Mathf.IsPowerOfTwo(header.biHeight)) { if (!premultiplyAlpha) { dib = FreeImage.ConvertToGreyscale(dib); } } else { //int w = Mathf.NextPowerOfTwo(header.biWidth); //int h = Mathf.NextPowerOfTwo(header.biHeight); //FIBITMAP bitmap2 = FreeImage.Allocate(w, h, 8); //FreeImage.Paste(bitmap2, dib, 0, 0, 255); //FreeImage.UnloadEx(ref dib); //dib = bitmap2; forceGrayscaleAfterTexture2D = true; dib = FreeImage.ConvertTo32Bits(dib); } } else { dib = FreeImage.ConvertTo32Bits(dib); } destType = FREE_IMAGE_TYPE.FIT_BITMAP; break; } //// premultiply if need be //if (premultiplyAlpha) // FreeImage.PreMultiplyWithAlpha(dib); // convert to destination expected type if (destType != FREE_IMAGE_TYPE.FIT_UNKNOWN && origInputType != destType) { Debug.Log("Trying to convert from:" + origInputType + ", to:" + destType); dib = FreeImage.ConvertToType(dib, destType, false); } //GC.Collect(); Debug.Log("Used Heap Size After FreeImage.ConvertToType: " + Profiler.GetMonoUsedSizeLong() / 1024 / 1024); //if (isModifiedEXR && origInputType == FREE_IMAGE_TYPE.FIT_FLOAT) width = (int)FreeImageAPI.FreeImage.GetWidth(dib); height = (int)FreeImageAPI.FreeImage.GetHeight(dib); uint bpp = FreeImage.GetBPP(dib); int pitch = (int)FreeImage.GetPitch(dib); long byteSize = pitch * height; Debug.Log("byteSize: " + byteSize); FREE_IMAGE_TYPE inputType = FreeImage.GetImageType(dib); if (doMipMaps) { byteSize = (long)(byteSize * 1.6666f); } //bytes = new byte[byteSize]; FreeImage.ConvertToRawBits(bytes, dib, pitch, bpp, 0, 0, 0, false); Debug.Log("Used Heap Size After FreeImage.ConvertToRawBits: " + Profiler.GetMonoUsedSizeLong() / 1024 / 1024); FreeImage.UnloadEx(ref dib); //GC.Collect(); //Debug.Log("Used Heap Size After FreeImage.UnloadEx: " + Profiler.GetMonoUsedSizeLong() / 1024 / 1024); // choose texture format formatToLoad = TextureFormat.ARGB32; Debug.Log("inputType:" + inputType); switch (inputType) { case FREE_IMAGE_TYPE.FIT_FLOAT: formatToLoad = TextureFormat.RFloat; if (origInputType == FREE_IMAGE_TYPE.FIT_UINT16) { rtFormat = RenderTextureFormat.RHalf; } else { rtFormat = RenderTextureFormat.RFloat; } break; case FREE_IMAGE_TYPE.FIT_UINT16: formatToLoad = TextureFormat.RHalf; rtFormat = RenderTextureFormat.RHalf; break; case FREE_IMAGE_TYPE.FIT_RGBA16: formatToLoad = TextureFormat.RGBAHalf; rtFormat = RenderTextureFormat.ARGBHalf; isLinear = true; break; case FREE_IMAGE_TYPE.FIT_RGBAF: formatToLoad = TextureFormat.RGBAFloat; if (origInputType == FREE_IMAGE_TYPE.FIT_RGBA16 || origInputType == FREE_IMAGE_TYPE.FIT_RGB16) { rtFormat = RenderTextureFormat.ARGBHalf; } else { rtFormat = RenderTextureFormat.ARGBFloat; } isLinear = true; break; case FREE_IMAGE_TYPE.FIT_BITMAP: //Iterate over all scanlines switch (bpp) { case 8: { formatToLoad = TextureFormat.Alpha8; rtFormat = RenderTextureFormat.R8; } break; case 16: formatToLoad = TextureFormat.RGBA4444; rtFormat = RenderTextureFormat.ARGB4444; break; case 24: if (FreeImage.IsLittleEndian()) { int length = bytes.Length; // make sure it's a multiple of 3 int factor = length / 3; int adjustedLength = factor * 3; // convert back to RGB for (int i = 0; i < adjustedLength; i += 3) { // convert BGR to RGB var r = bytes[i]; bytes[i] = bytes[i + 2]; bytes[i + 2] = r; } } formatToLoad = TextureFormat.RGB24; rtFormat = RenderTextureFormat.ARGB32; break; case 32: if (forceGrayscaleAfterTexture2D) { formatToLoad = TextureFormat.ARGB32; rtFormat = RenderTextureFormat.R8; } else { if (FreeImage.IsLittleEndian()) { int length = bytes.Length; // make sure it's a multiple of 4 int factor = length / 4; int adjustedLength = factor * 4; for (int j = 0; j < adjustedLength; j += 4) { // convert BGRA to ARGB var a = bytes[j]; var r = bytes[j + 1]; bytes[j] = bytes[j + 3]; bytes[j + 1] = bytes[j + 2]; bytes[j + 2] = r; bytes[j + 3] = a; } } formatToLoad = TextureFormat.ARGB32; rtFormat = RenderTextureFormat.ARGB32; } break; } break; } successfullyLoadedRaw = true; } catch (System.Exception ex) { Debug.LogError("Exception: " + ex.Message); } } //); //newThread.IsBackground = true; //newThread.Start(); //newThread.Join(); //outBPP = origBPP; if (successfullyLoadedRaw) { RenderTexture temp = LoadRawToTexture2D(bytes, width, height, formatToLoad, rtFormat, doMipMaps, isLinear, forceGC, premultiplyAlpha); //GC.Collect(); Debug.Log("Used Heap Size After LoadRawToTexture2D: " + Profiler.GetMonoUsedSizeLong() / 1024 / 1024); return(temp); } return(null); }