4 using System.Collections.Generic;
5 using System.Runtime.InteropServices;
8 using SiliconStudio.Core;
9 using SiliconStudio.Core.Diagnostics;
10 using SiliconStudio.TextureConverter.Requests;
12 using FreeImageAPI.Plugins;
14 namespace SiliconStudio.TextureConverter.TexLibraries
19 internal class FreeImageTextureLibraryData : ITextureLibraryData
24 public FIBITMAP[] Bitmaps {
get; set; }
29 public IntPtr Data {
get; set; }
35 internal class FITexLib : ITexLibrary
37 private static Logger Log = GlobalLogger.GetLogger(
"FITexLib");
47 public void Dispose() {}
49 public void Dispose(TexImage image)
51 FreeImageTextureLibraryData libraryData = (FreeImageTextureLibraryData) image.LibraryData[
this];
52 if (libraryData.Data != IntPtr.Zero) Marshal.FreeHGlobal(libraryData.Data);
55 public void StartLibrary(TexImage image)
57 if(Tools.IsCompressed(image.Format))
59 Log.Error(
"FreeImage can't process compressed texture.");
60 throw new TextureToolsException(
"FreeImage can't process compressed texture.");
63 FreeImageTextureLibraryData libraryData =
new FreeImageTextureLibraryData();
64 image.LibraryData[
this] = libraryData;
66 libraryData.Bitmaps =
new FIBITMAP[image.SubImageArray.Length];
67 uint bpp = Tools.GetBPP(image.Format);
69 for (
int i = 0; i < image.SubImageArray.Length; ++i)
71 libraryData.Bitmaps[i] = FreeImage.ConvertFromRawBits(image.SubImageArray[i].Data, FREE_IMAGE_TYPE.FIT_BITMAP, image.SubImageArray[i].Width, image.SubImageArray[i].Height, image.SubImageArray[i].RowPitch, bpp, 0x00FF0000, 0x0000FF00, 0x000000FF,
false);
74 if (image.DisposingLibrary != null) image.DisposingLibrary.Dispose(image);
76 image.DisposingLibrary =
this;
78 libraryData.Data = IntPtr.Zero;
81 public void EndLibrary(TexImage image)
83 if (!image.LibraryData.ContainsKey(
this))
return;
84 FreeImageTextureLibraryData libraryData = (FreeImageTextureLibraryData)image.LibraryData[
this];
86 IntPtr buffer = Marshal.AllocHGlobal(image.DataSize);
88 int size, rowPitch, slicePitch;
90 image.SubImageArray =
new TexImage.SubImage[libraryData.Bitmaps.Length];
94 for (
int i = 0; i < libraryData.Bitmaps.Length; ++i)
96 image.SubImageArray[i].Width = (int)FreeImage.GetWidth(libraryData.Bitmaps[i]);
97 image.SubImageArray[i].Height = (int)FreeImage.GetHeight(libraryData.Bitmaps[i]);
99 Tools.ComputePitch(image.Format, image.SubImageArray[i].Width, image.SubImageArray[i].Height, out rowPitch, out slicePitch);
102 image.SubImageArray[i].Data =
new IntPtr(buffer.ToInt64() + offset);
103 image.SubImageArray[i].DataSize =
size;
104 image.SubImageArray[i].RowPitch = rowPitch;
105 image.SubImageArray[i].SlicePitch = slicePitch;
107 Utilities.CopyMemory(image.SubImageArray[i].Data, FreeImage.GetBits(libraryData.Bitmaps[i]), size);
111 catch (AccessViolationException e)
113 Log.Error(
"Failed to convert FreeImage native data to TexImage texture. ", e);
114 throw new TextureToolsException(
"Failed to convert FreeImage native data to TexImage texture. ", e);
117 image.Data = image.SubImageArray[0].Data;
118 libraryData.Data = image.Data;
121 for (
int i = 0; i < libraryData.Bitmaps.Length; ++i)
123 FreeImage.Unload(libraryData.Bitmaps[i]);
126 libraryData.Bitmaps = null;
127 image.DisposingLibrary =
this;
130 public bool CanHandleRequest(TexImage image, IRequest request)
132 switch (request.Type)
134 case RequestType.Loading:
136 LoadingRequest loader = (LoadingRequest)request;
138 return format != FREE_IMAGE_FORMAT.FIF_UNKNOWN && format != FREE_IMAGE_FORMAT.FIF_DDS;
140 case RequestType.Export:
142 ExportRequest export = (ExportRequest)request;
144 return format != FREE_IMAGE_FORMAT.FIF_UNKNOWN && format != FREE_IMAGE_FORMAT.FIF_DDS;
146 case RequestType.Rescaling:
147 RescalingRequest rescale = (RescalingRequest)request;
148 return rescale.Filter != Filter.Rescaling.Nearest;
150 case RequestType.SwitchingChannels:
151 case RequestType.GammaCorrection:
152 case RequestType.Flipping:
153 case RequestType.FlippingSub:
154 case RequestType.Swapping:
161 public void Execute(TexImage image, IRequest request)
163 FreeImageTextureLibraryData libraryData = image.LibraryData.ContainsKey(
this) ? (FreeImageTextureLibraryData)image.LibraryData[this] : null;
167 case RequestType.Loading:
168 Load(image, libraryData, (LoadingRequest)request);
171 case RequestType.Rescaling:
172 Rescale(image, libraryData, (RescalingRequest)request);
175 case RequestType.SwitchingChannels:
176 SwitchChannels(image, libraryData, (SwitchingBRChannelsRequest)request);
179 case RequestType.Flipping:
180 Flip(image, libraryData, (FlippingRequest)request);
183 case RequestType.FlippingSub:
184 FlipSub(image, libraryData, (FlippingSubRequest)request);
187 case RequestType.Swapping:
188 Swap(image, libraryData, (SwappingRequest)request);
191 case RequestType.Export:
192 Export(image, libraryData, (ExportRequest)request);
195 case RequestType.GammaCorrection:
196 CorrectGamma(image, libraryData, (GammaCorrectionRequest)request);
200 Log.Error(
"FITexLib (FreeImage) can't handle this request: " + request.Type);
201 throw new TextureToolsException(
"FITexLib (FreeImage) can't handle this request: " + request.Type);
213 private void Load(TexImage image, FreeImageTextureLibraryData libraryData, LoadingRequest loader)
215 Log.Info(
"Loading " + loader.FilePath +
" ...");
220 temp = FreeImage.LoadEx(loader.FilePath);
221 FreeImage.FlipVertical(temp);
224 throw new Exception(
"FreeImage's image data is null");
228 Log.Error(
"Loading file " + loader.FilePath +
" failed: " + e.Message);
229 throw new TextureToolsException(
"Loading file " + loader.FilePath +
" failed: " + e.Message);
233 libraryData =
new FreeImageTextureLibraryData { Bitmaps =
new FIBITMAP[1] { FreeImage.ConvertTo32Bits(temp) } };
234 image.LibraryData[
this] = libraryData;
236 FreeImage.Unload(temp);
238 image.Data = FreeImage.GetBits(libraryData.Bitmaps[0]);
239 image.Width = (int)FreeImage.GetWidth(libraryData.Bitmaps[0]);
240 image.Height = (int)FreeImage.GetHeight(libraryData.Bitmaps[0]);
242 image.Dimension = image.Height == 1 ? TexImage.TextureDimension.Texture1D : TexImage.TextureDimension.Texture2D;
243 image.Format = SiliconStudio.Paradox.Graphics.PixelFormat.B8G8R8A8_UNorm;
245 int rowPitch, slicePitch;
246 Tools.ComputePitch(image.Format, image.Width, image.Height, out rowPitch, out slicePitch);
247 image.RowPitch = rowPitch;
248 image.SlicePitch = slicePitch;
251 image.SubImageArray[0].Data = image.Data;
252 image.SubImageArray[0].DataSize = image.DataSize;
253 image.SubImageArray[0].Width = image.Width;
254 image.SubImageArray[0].Height = image.Height;
255 image.SubImageArray[0].RowPitch = rowPitch;
256 image.SubImageArray[0].SlicePitch = slicePitch;
257 image.DataSize = (int) (FreeImage.GetDIBSize(libraryData.Bitmaps[0]) - GetHeaderSize());
258 libraryData.Data = IntPtr.Zero;
259 image.DisposingLibrary =
this;
266 private unsafe uint GetHeaderSize()
281 private void Rescale(TexImage image, FreeImageTextureLibraryData libraryData, RescalingRequest rescale)
283 int width = rescale.ComputeWidth(image);
284 int height = rescale.ComputeHeight(image);
286 Log.Info(
"Rescaling image to " + width +
"x" + height +
" with " + rescale.Filter +
" ...");
290 if (image.Dimension == TexImage.TextureDimension.Texture3D)
292 newTab =
new FIBITMAP[image.ArraySize * image.FaceCount * image.Depth];
295 int nbSubImageWithMipMapPerArrayMemeber = 0;
296 curDepth = image.Depth;
297 for (
int i = 0; i < image.MipmapCount; ++i)
299 nbSubImageWithMipMapPerArrayMemeber += curDepth;
300 curDepth = curDepth > 1 ? curDepth >>= 1 : curDepth;
304 for (
int j = 0; j < image.ArraySize; ++j)
306 for (
int i = 0; i < image.Depth; ++i)
308 newTab[ct] = FreeImage.Rescale(libraryData.Bitmaps[i + j * nbSubImageWithMipMapPerArrayMemeber], width, height, (
FREE_IMAGE_FILTER)rescale.Filter);
315 newTab =
new FIBITMAP[image.ArraySize];
317 for (
int i = 0; i < libraryData.Bitmaps.Length; i += image.MipmapCount)
319 newTab[ct] = FreeImage.Rescale(libraryData.Bitmaps[i], width, height, (
FREE_IMAGE_FILTER)rescale.Filter);
324 for (
int i = 0; i < libraryData.Bitmaps.Length; ++i)
326 FreeImage.Unload(libraryData.Bitmaps[i]);
329 libraryData.Bitmaps = newTab;
330 image.Data = FreeImage.GetBits(newTab[0]);
333 image.Rescale(width, height);
335 int rowPitch, slicePitch;
336 Tools.ComputePitch(image.Format, width, height, out rowPitch, out slicePitch);
338 image.RowPitch = rowPitch;
339 image.SlicePitch = slicePitch;
340 image.MipmapCount = 1;
341 image.DataSize = image.SlicePitch * image.ArraySize * image.FaceCount * image.Depth;
354 private void SwitchChannels(TexImage image, FreeImageTextureLibraryData libraryData, SwitchingBRChannelsRequest switchC)
357 Log.Info(
"Switching channels R and G ...");
359 for (
int i = 0; i < libraryData.Bitmaps.Length; ++i)
361 FIBITMAP blueChannel = FreeImage.GetChannel(libraryData.Bitmaps[i], FREE_IMAGE_COLOR_CHANNEL.FICC_BLUE);
362 FIBITMAP redChannel = FreeImage.GetChannel(libraryData.Bitmaps[i], FREE_IMAGE_COLOR_CHANNEL.FICC_RED);
363 FreeImage.SetChannel(libraryData.Bitmaps[i], redChannel, FREE_IMAGE_COLOR_CHANNEL.FICC_BLUE);
364 FreeImage.SetChannel(libraryData.Bitmaps[i], blueChannel, FREE_IMAGE_COLOR_CHANNEL.FICC_RED);
365 FreeImage.Unload(blueChannel);
366 FreeImage.Unload(redChannel);
369 if (Tools.IsInBGRAOrder(image.Format))
370 image.Format = SiliconStudio.Paradox.Graphics.PixelFormat.R8G8B8A8_UNorm;
372 image.Format = SiliconStudio.Paradox.Graphics.PixelFormat.B8G8R8A8_UNorm;
375 public bool SupportBGRAOrder()
387 private void Flip(TexImage image, FreeImageTextureLibraryData libraryData, FlippingRequest flip)
389 Log.Info(
"Flipping image : " + flip.Flip +
" ...");
391 for (
int i = 0; i < libraryData.Bitmaps.Length; ++i)
395 case Orientation.Vertical:
396 FreeImage.FlipVertical(libraryData.Bitmaps[i]);
399 case Orientation.Horizontal:
400 FreeImage.FlipHorizontal(libraryData.Bitmaps[i]);
405 image.Flip(flip.Flip);
415 private void FlipSub(TexImage image, FreeImageTextureLibraryData libraryData, FlippingSubRequest flipSub)
417 Log.Info(
"Flipping image : sub-image " + flipSub.SubImageIndex +
" " + flipSub.Flip +
" ...");
419 if (flipSub.SubImageIndex >= 0 && flipSub.SubImageIndex < libraryData.Bitmaps.Length)
421 switch (flipSub.Flip)
423 case Orientation.Vertical:
424 FreeImage.FlipVertical(libraryData.Bitmaps[flipSub.SubImageIndex]);
427 case Orientation.Horizontal:
428 FreeImage.FlipHorizontal(libraryData.Bitmaps[flipSub.SubImageIndex]);
434 Log.Warning(
"Cannot flip the sub-image " + flipSub.SubImageIndex +
" because there is only " + libraryData.Bitmaps.Length +
" sub-images.");
447 private void Swap(TexImage image, FreeImageTextureLibraryData libraryData, SwappingRequest swap)
449 Log.Info(
"Swapping image : sub-image " + swap.FirstSubImageIndex +
" and " + swap.SecondSubImageIndex +
" ...");
451 if (swap.FirstSubImageIndex >= 0 && swap.FirstSubImageIndex < libraryData.Bitmaps.Length
452 && swap.SecondSubImageIndex >= 0 && swap.SecondSubImageIndex < libraryData.Bitmaps.Length)
455 var firstImage = FreeImage.Copy(libraryData.Bitmaps[swap.FirstSubImageIndex], 0, 0, image.Width, image.Height);
456 FreeImage.Paste(libraryData.Bitmaps[swap.FirstSubImageIndex], libraryData.Bitmaps[swap.SecondSubImageIndex], 0, 0, 256);
457 FreeImage.Paste(libraryData.Bitmaps[swap.SecondSubImageIndex], firstImage, 0, 0, 256);
463 Log.Warning(
"Cannot swap the sub-images " + swap.FirstSubImageIndex +
" and " + swap.SecondSubImageIndex +
" because there is only " + libraryData.Bitmaps.Length +
" sub-images.");
482 private void Export(TexImage image, FreeImageTextureLibraryData libraryData, ExportRequest request)
484 String directory = Path.GetDirectoryName(request.FilePath);
485 String fileName = Path.GetFileNameWithoutExtension(request.FilePath);
486 String extension = Path.GetExtension(request.FilePath);
489 if (image.Dimension == TexImage.TextureDimension.Texture3D)
491 Log.Error(
"Not implemented.");
492 throw new TextureToolsException(
"Not implemented.");
495 if(!Tools.IsInBGRAOrder(image.Format))
497 SwitchChannels(image, libraryData,
new SwitchingBRChannelsRequest());
500 if (image.SubImageArray.Length > 1 && request.MinimumMipMapSize < FreeImage.GetWidth(libraryData.Bitmaps[0]) && request.MinimumMipMapSize < FreeImage.GetHeight(libraryData.Bitmaps[0]))
503 for (
int i = 0; i < image.ArraySize; ++i)
505 for (
int j = 0; j < image.MipmapCount; ++j)
507 if (FreeImage.GetWidth(libraryData.Bitmaps[imageCount]) < request.MinimumMipMapSize || FreeImage.GetHeight(libraryData.Bitmaps[imageCount]) < request.MinimumMipMapSize)
510 finalName = directory +
"/" + fileName +
"-ind_" + i +
"-mip_" + j + extension;
511 FreeImage.FlipVertical(libraryData.Bitmaps[imageCount]);
512 if (!FreeImage.SaveEx(libraryData.Bitmaps[imageCount], finalName))
514 Log.Error(
"Export failure.");
515 throw new TextureToolsException(
"Export failure.");
517 FreeImage.FlipVertical(libraryData.Bitmaps[imageCount]);
518 Log.Info(
"Exporting image to " + finalName +
" ...");
525 FreeImage.FlipVertical(libraryData.Bitmaps[0]);
526 if (!FreeImage.SaveEx(libraryData.Bitmaps[0], request.FilePath))
528 Log.Error(
"Export failure.");
529 throw new TextureToolsException(
"Export failure.");
531 FreeImage.FlipVertical(libraryData.Bitmaps[0]);
532 Log.Info(
"Exporting image to " + request.FilePath +
" ...");
535 image.Save(request.FilePath);
545 public void CorrectGamma(TexImage image, FreeImageTextureLibraryData libraryData, GammaCorrectionRequest request)
547 Log.Info(
"Applying a gamma correction of " + request.Gamma +
" ...");
549 foreach (
FIBITMAP bitmap
in libraryData.Bitmaps)
551 FreeImage.AdjustGamma(bitmap, request.Gamma);
The FIBITMAP structure is a handle to a FreeImage bimtap.
Base implementation for ILogger.
FREE_IMAGE_FILTER
Upsampling / downsampling filters. Constants used in FreeImage_Rescale.
This structure contains information about the dimensions and color format of a device-independent bit...
_In_ size_t _In_ size_t _In_ DXGI_FORMAT format
FREE_IMAGE_FORMAT
I/O image format identifiers.
_In_ size_t _In_ size_t size
Output message to log right away.