Skip to content

Commit 86b301a

Browse files
committed
Fix performance issue when importing PNG without alpha channel
1 parent 21b406b commit 86b301a

File tree

2 files changed

+75
-38
lines changed

2 files changed

+75
-38
lines changed

Runtime/AsyncImageLoader/FreeImage.cs

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,6 @@
33

44
public static partial class AsyncImageLoader {
55
public class FreeImage {
6-
public enum Filter {
7-
FILTER_BOX = 0,
8-
FILTER_BICUBIC = 1,
9-
FILTER_BILINEAR = 2,
10-
FILTER_BSPLINE = 3,
11-
FILTER_CATMULLROM = 4,
12-
FILTER_LANCZOS3 = 5
13-
}
14-
156
public enum Format {
167
FIF_UNKNOWN = -1,
178
FIF_BMP = 0,
@@ -54,6 +45,31 @@ public enum Format {
5445
FIF_JXR = 36
5546
}
5647

48+
internal enum Type {
49+
FIT_UNKNOWN = 0,
50+
FIT_BITMAP = 1,
51+
FIT_UINT16 = 2,
52+
FIT_INT16 = 3,
53+
FIT_UINT32 = 4,
54+
FIT_INT32 = 5,
55+
FIT_FLOAT = 6,
56+
FIT_DOUBLE = 7,
57+
FIT_COMPLEX = 8,
58+
FIT_RGB16 = 9,
59+
FIT_RGBA16 = 10,
60+
FIT_RGBF = 11,
61+
FIT_RGBAF = 12
62+
}
63+
64+
internal enum ColorType {
65+
FIC_MINISWHITE = 0,
66+
FIC_MINISBLACK = 1,
67+
FIC_RGB = 2,
68+
FIC_PALETTE = 3,
69+
FIC_RGBALPHA = 4,
70+
FIC_CMYK = 5
71+
}
72+
5773
const string FreeImageLibrary = "FreeImage";
5874

5975
[DllImport(FreeImageLibrary, EntryPoint = "FreeImage_GetFileTypeFromMemory")]
@@ -80,7 +96,10 @@ public enum Format {
8096
[DllImport(FreeImageLibrary, EntryPoint = "FreeImage_GetHeight")]
8197
internal static extern uint GetHeight(IntPtr handle);
8298

83-
[DllImport(FreeImageLibrary, EntryPoint = "FreeImage_IsTransparent")]
84-
internal static extern bool IsTransparent(IntPtr dib);
99+
[DllImport(FreeImageLibrary, EntryPoint = "FreeImage_GetImageType")]
100+
internal static extern Type GetImageType(IntPtr dib);
101+
102+
[DllImport(FreeImageLibrary, EntryPoint = "FreeImage_GetBPP")]
103+
internal static extern int GetBPP(IntPtr dib);
85104
}
86105
}

Runtime/ImageImporter.cs

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System;
1+
using System;
22
using System.Runtime.InteropServices;
33
using System.Threading.Tasks;
44
using Unity.Burst;
@@ -33,7 +33,7 @@ class ImageImporter : IDisposable {
3333
IntPtr _bitmap;
3434
int _width;
3535
int _height;
36-
bool _isTransparent;
36+
TextureFormat _textureFormat;
3737
int _pixelSize; // In bytes
3838

3939
JobHandle _finalJob;
@@ -68,8 +68,7 @@ public ImageImporter(byte[] imageData, LoaderSettings loaderSettings) {
6868
throw new Exception("Texture size exceed maximum dimension supported by Unity.");
6969
}
7070

71-
_isTransparent = FreeImage.IsTransparent(_bitmap);
72-
_pixelSize = _isTransparent ? 4 : 3;
71+
DetermineTextureFormat();
7372
} finally {
7473
if (memoryStream != IntPtr.Zero) FreeImage.CloseMemory(memoryStream);
7574
}
@@ -86,11 +85,7 @@ public void Dispose() {
8685
public Texture2D CreateNewTexture() {
8786
using (CreateNewTextureMarker.Auto()) {
8887
var mipmapCount = CalculateMipmapCount();
89-
var texture = new Texture2D(
90-
_width, _height,
91-
_isTransparent ? TextureFormat.RGBA32 : TextureFormat.RGB24,
92-
mipmapCount, _loaderSettings.linear
93-
);
88+
var texture = new Texture2D(_width, _height, _textureFormat, mipmapCount, _loaderSettings.linear);
9489
var rawTextureView = texture.GetRawTextureData<byte>();
9590
LoadRawTextureData(rawTextureView);
9691
ProcessRawTextureData(rawTextureView, mipmapCount);
@@ -102,11 +97,7 @@ public Texture2D CreateNewTexture() {
10297

10398
public async Task<Texture2D> CreateNewTextureAsync() {
10499
var mipmapCount = CalculateMipmapCount();
105-
var texture = new Texture2D(
106-
_width, _height,
107-
_isTransparent ? TextureFormat.RGBA32 : TextureFormat.RGB24,
108-
mipmapCount, _loaderSettings.linear
109-
);
100+
var texture = new Texture2D(_width, _height, _textureFormat, mipmapCount, _loaderSettings.linear);
110101
var rawTextureView = texture.GetRawTextureData<byte>();
111102
await Task.Run(() => LoadRawTextureData(rawTextureView));
112103
ProcessRawTextureData(rawTextureView, mipmapCount);
@@ -118,11 +109,7 @@ public async Task<Texture2D> CreateNewTextureAsync() {
118109
public void LoadIntoTexture(Texture2D texture) {
119110
using (LoadIntoTextureMarker.Auto()) {
120111
var mipmapCount = CalculateMipmapCount(true);
121-
texture.Resize(
122-
_width, _height,
123-
_isTransparent ? TextureFormat.RGBA32 : TextureFormat.RGB24,
124-
_loaderSettings.generateMipmap
125-
);
112+
texture.Resize(_width, _height, _textureFormat, _loaderSettings.generateMipmap);
126113
var rawTextureView = texture.GetRawTextureData<byte>();
127114
LoadRawTextureData(rawTextureView);
128115
ProcessRawTextureData(rawTextureView, mipmapCount);
@@ -133,11 +120,7 @@ public void LoadIntoTexture(Texture2D texture) {
133120

134121
public async Task LoadIntoTextureAsync(Texture2D texture) {
135122
var mipmapCount = CalculateMipmapCount(true);
136-
texture.Resize(
137-
_width, _height,
138-
_isTransparent ? TextureFormat.RGBA32 : TextureFormat.RGB24,
139-
_loaderSettings.generateMipmap
140-
);
123+
texture.Resize(_width, _height, _textureFormat, _loaderSettings.generateMipmap);
141124
var rawTextureView = texture.GetRawTextureData<byte>();
142125
await Task.Run(() => LoadRawTextureData(rawTextureView));
143126
ProcessRawTextureData(rawTextureView, mipmapCount);
@@ -171,6 +154,41 @@ Vector2Int CalculateMipmapDimensions(int mipmapLevel) {
171154
}
172155
}
173156

157+
void DetermineTextureFormat() {
158+
var type = FreeImage.GetImageType(_bitmap);
159+
160+
switch (type) {
161+
case FreeImage.Type.FIT_BITMAP:
162+
var bpp = FreeImage.GetBPP(_bitmap);
163+
164+
switch (bpp) {
165+
case 24:
166+
_textureFormat = TextureFormat.RGB24;
167+
_pixelSize = 3;
168+
break;
169+
case 32:
170+
_textureFormat = TextureFormat.RGBA32;
171+
_pixelSize = 4;
172+
break;
173+
default:
174+
throw new Exception($"Bitmap bitdepth not supported: {bpp}");
175+
}
176+
break;
177+
case FreeImage.Type.FIT_RGB16:
178+
case FreeImage.Type.FIT_RGBF:
179+
_textureFormat = TextureFormat.RGB24;
180+
_pixelSize = 3;
181+
break;
182+
case FreeImage.Type.FIT_RGBA16:
183+
case FreeImage.Type.FIT_RGBAF:
184+
_textureFormat = TextureFormat.RGBA32;
185+
_pixelSize = 4;
186+
break;
187+
default:
188+
throw new Exception($"Image type not supported: {type}");
189+
}
190+
}
191+
174192
void LoadRawTextureData(NativeArray<byte> rawTextureView) {
175193
using (LoadRawTextureDataMarker.Auto()) {
176194
var mipmapDimensions = CalculateMipmapDimensions(0);
@@ -181,7 +199,7 @@ void LoadRawTextureData(NativeArray<byte> rawTextureView) {
181199
FreeImage.ConvertToRawBits(
182200
(IntPtr)mipmapSlice.GetUnsafePtr(), _bitmap,
183201
_pixelSize * mipmapDimensions.x,
184-
_isTransparent ? 32u : 24u,
202+
_textureFormat == TextureFormat.RGBA32 ? 32u : 24u,
185203
0, 0, 0, false
186204
);
187205
}
@@ -195,7 +213,7 @@ void ProcessRawTextureData(NativeArray<byte> rawTextureView, int mipmapCount) {
195213
var mipmapSlice = new NativeSlice<byte>(rawTextureView, 0, _pixelSize * mipmapSize);
196214
var mipmapIndex = _pixelSize * mipmapSize;
197215

198-
_finalJob = _isTransparent ?
216+
_finalJob = _textureFormat == TextureFormat.RGBA32 ?
199217
new SwapGBRA32ToRGBA32Job {
200218
mipmapSlice = mipmapSlice
201219
}.Schedule(mipmapSize, 8192) :
@@ -208,7 +226,7 @@ void ProcessRawTextureData(NativeArray<byte> rawTextureView, int mipmapCount) {
208226
mipmapSize = nextMipmapDimensions.x * nextMipmapDimensions.y;
209227
var nextMipmapSlice = new NativeSlice<byte>(rawTextureView, mipmapIndex, _pixelSize * mipmapSize);
210228
mipmapIndex += _pixelSize * mipmapSize;
211-
_finalJob = _isTransparent ?
229+
_finalJob = _textureFormat == TextureFormat.RGBA32 ?
212230
new FilterMipmapRGBA32Job {
213231
inputWidth = mipmapDimensions.x,
214232
inputHeight = mipmapDimensions.y,

0 commit comments

Comments
 (0)