回答編集履歴
3
Unsafe, Span を比較
answer
CHANGED
@@ -1,63 +1,119 @@
|
|
1
1
|
こんなもんでしょうか?
|
2
|
-
Span<T> が使えるともっと速くなります。
|
3
2
|
|
4
3
|
```csharp
|
5
4
|
using System;
|
5
|
+
using System.Diagnostics;
|
6
6
|
using System.IO;
|
7
7
|
using System.Runtime.InteropServices;
|
8
8
|
using System.Windows.Media.Imaging;
|
9
9
|
|
10
|
+
public static class BitmapImageHelper
|
11
|
+
{
|
10
|
-
[StructLayout(LayoutKind.Sequential, Pack = 2)]
|
12
|
+
[StructLayout(LayoutKind.Sequential, Pack = 2)]
|
11
|
-
private struct BITMAPFILEHEADER
|
13
|
+
private struct BITMAPFILEHEADER
|
14
|
+
{
|
12
|
-
|
15
|
+
public ushort bfType;
|
13
|
-
|
16
|
+
public uint bfSize;
|
14
|
-
|
17
|
+
public ushort bfReserved1;
|
15
|
-
|
18
|
+
public ushort bfReserved2;
|
16
|
-
|
19
|
+
public uint bfOffBits;
|
20
|
+
}
|
21
|
+
|
22
|
+
[StructLayout(LayoutKind.Sequential)]
|
23
|
+
private struct BITMAPINFOHEADER
|
24
|
+
{
|
25
|
+
public uint biSize;
|
26
|
+
public int biWidth;
|
27
|
+
public int biHeight;
|
28
|
+
public ushort biPlanes;
|
29
|
+
public ushort biBitCount;
|
30
|
+
public uint biCompression;
|
31
|
+
public uint biSizeImage;
|
32
|
+
public int biXPelsPerMeter;
|
33
|
+
public int biYPelsPerMeter;
|
34
|
+
public uint biClrUsed;
|
35
|
+
public uint biClrImportant;
|
36
|
+
}
|
37
|
+
|
38
|
+
public enum BitmapDirection
|
39
|
+
{
|
40
|
+
TopDown, BottomUp
|
41
|
+
}
|
42
|
+
|
43
|
+
public static BitmapImage BitmapImageFromFile(
|
44
|
+
string fileName,
|
45
|
+
out BitmapDirection direction) {
|
46
|
+
byte[] bytes = File.ReadAllBytes(fileName);
|
47
|
+
direction = GetBitmapDirection(bytes);
|
48
|
+
using (MemoryStream mem = new MemoryStream(bytes)) {
|
49
|
+
BitmapImage bitmap = new BitmapImage();
|
50
|
+
bitmap.BeginInit();
|
51
|
+
bitmap.StreamSource = mem;
|
52
|
+
bitmap.CacheOption = BitmapCacheOption.OnLoad;
|
53
|
+
bitmap.EndInit();
|
54
|
+
bitmap.Freeze();
|
55
|
+
return bitmap;
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
public static BitmapDirection GetBitmapDirection(byte[] bytes) {
|
60
|
+
int offset = Marshal.SizeOf(typeof(BITMAPFILEHEADER));
|
61
|
+
int size = Marshal.SizeOf(typeof(BITMAPINFOHEADER));
|
62
|
+
IntPtr ptr = Marshal.AllocCoTaskMem(size);
|
63
|
+
try {
|
64
|
+
Marshal.Copy(bytes, offset, ptr, size);
|
65
|
+
BITMAPINFOHEADER infoHeader = Marshal.PtrToStructure<BITMAPINFOHEADER>(ptr);
|
66
|
+
return infoHeader.biHeight > 0 ? BitmapDirection.BottomUp
|
67
|
+
: BitmapDirection.TopDown;
|
68
|
+
} finally {
|
69
|
+
Marshal.FreeCoTaskMem(ptr);
|
70
|
+
}
|
71
|
+
}
|
17
72
|
}
|
73
|
+
```
|
18
74
|
|
19
|
-
[StructLayout(LayoutKind.Sequential)]
|
20
|
-
|
75
|
+
Unsafe 版とSpan版を作って比較してみました。
|
21
|
-
|
76
|
+
```csharp
|
22
|
-
public int biWidth;
|
23
|
-
public int biHeight;
|
24
|
-
|
77
|
+
// Unsafe 版
|
78
|
+
public static unsafe BitmapDirection UnsafeGetBitmapDirection(byte[] bytes) {
|
25
|
-
|
79
|
+
fixed (byte* lpFileHeader = bytes) {
|
80
|
+
BITMAPINFOHEADER* lpInfoHeader =
|
81
|
+
(BITMAPINFOHEADER*)(lpFileHeader + sizeof(BITMAPFILEHEADER));
|
82
|
+
return lpInfoHeader->biHeight > 0 ? BitmapDirection.BottomUp
|
26
|
-
|
83
|
+
: BitmapDirection.TopDown;
|
27
|
-
public uint biSizeImage;
|
28
|
-
public int biXPelsPerMeter;
|
29
|
-
public int biYPelsPerMeter;
|
30
|
-
|
84
|
+
}
|
31
|
-
public uint biClrImportant;
|
32
85
|
}
|
33
86
|
|
87
|
+
// Span 版
|
88
|
+
public static BitmapDirection SpanGetBitmapDirection(byte[] bytes) {
|
89
|
+
int offset = Marshal.SizeOf(typeof(BITMAPFILEHEADER));
|
90
|
+
int size = Marshal.SizeOf(typeof(BITMAPINFOHEADER));
|
91
|
+
Span<byte> span = new Span<byte>(bytes, offset, size);
|
92
|
+
BITMAPINFOHEADER infoHeader = MemoryMarshal.Read<BITMAPINFOHEADER>(span);
|
93
|
+
return infoHeader.biHeight > 0 ? BitmapDirection.BottomUp
|
34
|
-
|
94
|
+
: BitmapDirection.TopDown;
|
35
|
-
{
|
36
|
-
TopDown, BottomUp
|
37
95
|
}
|
38
96
|
|
39
|
-
public static
|
97
|
+
public static void TryGetBitmapDirection(string fileName) {
|
40
|
-
string fileName,
|
41
|
-
out BitmapDirection direction) {
|
42
|
-
|
98
|
+
byte[] bytes = File.ReadAllBytes(fileName);
|
43
|
-
var ptr = Marshal.AllocHGlobal(bytes.Length);
|
44
|
-
try {
|
45
|
-
Marshal.Copy(bytes, 0, ptr, bytes.Length);
|
46
|
-
var offset = Marshal.SizeOf(typeof(BITMAPFILEHEADER));
|
47
|
-
var header = Marshal.PtrToStructure<BITMAPINFOHEADER>(ptr + offset);
|
48
|
-
|
99
|
+
Watch("GetBitmapDirection", () => { GetBitmapDirection(bytes); });
|
100
|
+
Watch("UnsafeGetBitmapDirection", () => { UnsafeGetBitmapDirection(bytes); });
|
101
|
+
Watch("SpanGetBitmapDirection", () => { SpanGetBitmapDirection(bytes); });
|
102
|
+
}
|
103
|
+
|
49
|
-
|
104
|
+
public static void Watch(string message, Action action) {
|
50
|
-
} finally {
|
51
|
-
|
105
|
+
Stopwatch sw = new Stopwatch();
|
106
|
+
sw.Start();
|
107
|
+
for (int i = 0; i < 100000; i++) {
|
108
|
+
action.Invoke();
|
52
109
|
}
|
53
|
-
var mem = new MemoryStream(bytes);
|
54
|
-
var bitmap = new BitmapImage();
|
55
|
-
bitmap.BeginInit();
|
56
|
-
bitmap.StreamSource = mem;
|
57
|
-
bitmap.CacheOption = BitmapCacheOption.OnLoad;
|
58
|
-
bitmap.EndInit();
|
59
|
-
|
110
|
+
sw.Stop();
|
60
|
-
|
111
|
+
Console.WriteLine($"{message} {sw.ElapsedMilliseconds}ms");
|
61
112
|
}
|
113
|
+
|
114
|
+
結果
|
115
|
+
GetBitmapDirection 61ms
|
116
|
+
UnsafeGetBitmapDirection 1ms
|
117
|
+
SpanGetBitmapDirection 12ms
|
62
118
|
```
|
63
|
-
|
119
|
+
Span も速いですが、やはり Unsafe は速かった。
|
2
逆だった
answer
CHANGED
@@ -45,8 +45,8 @@
|
|
45
45
|
Marshal.Copy(bytes, 0, ptr, bytes.Length);
|
46
46
|
var offset = Marshal.SizeOf(typeof(BITMAPFILEHEADER));
|
47
47
|
var header = Marshal.PtrToStructure<BITMAPINFOHEADER>(ptr + offset);
|
48
|
-
direction = header.biHeight >= 0 ? BitmapDirection.
|
48
|
+
direction = header.biHeight >= 0 ? BitmapDirection.BottomUp
|
49
|
-
: BitmapDirection.
|
49
|
+
: BitmapDirection.TopDown;
|
50
50
|
} finally {
|
51
51
|
Marshal.FreeHGlobal(ptr);
|
52
52
|
}
|
1
using
answer
CHANGED
@@ -2,6 +2,11 @@
|
|
2
2
|
Span<T> が使えるともっと速くなります。
|
3
3
|
|
4
4
|
```csharp
|
5
|
+
using System;
|
6
|
+
using System.IO;
|
7
|
+
using System.Runtime.InteropServices;
|
8
|
+
using System.Windows.Media.Imaging;
|
9
|
+
|
5
10
|
[StructLayout(LayoutKind.Sequential, Pack = 2)]
|
6
11
|
private struct BITMAPFILEHEADER {
|
7
12
|
public ushort bfType;
|
@@ -31,7 +36,7 @@
|
|
31
36
|
TopDown, BottomUp
|
32
37
|
}
|
33
38
|
|
34
|
-
public static BitmapImage
|
39
|
+
public static BitmapImage BitmapImageFromFile(
|
35
40
|
string fileName,
|
36
41
|
out BitmapDirection direction) {
|
37
42
|
var bytes = File.ReadAllBytes(fileName);
|