MyBitmap.vb 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. '一个万能的自动图片类型转换工具类
  2. Imports System.Drawing.Imaging
  3. Public Class MyBitmap
  4. ''' <summary>
  5. ''' 位图缓存。
  6. ''' </summary>
  7. Public Shared BitmapCache As New Concurrent.ConcurrentDictionary(Of String, MyBitmap)
  8. ''' <summary>
  9. ''' 存储的图片
  10. ''' </summary>
  11. Public Pic As System.Drawing.Bitmap
  12. '自动类型转换
  13. '支持的类:Image,ImageSource,Bitmap,ImageBrush,BitmapSource
  14. Public Shared Widening Operator CType(Image As System.Drawing.Image) As MyBitmap
  15. If Image Is Nothing Then Return Nothing
  16. Return New MyBitmap(Image)
  17. End Operator
  18. Public Shared Widening Operator CType(Image As MyBitmap) As System.Drawing.Image
  19. If Image Is Nothing Then Return Nothing
  20. Return Image.Pic
  21. End Operator
  22. Public Shared Widening Operator CType(Image As ImageSource) As MyBitmap
  23. If Image Is Nothing Then Return Nothing
  24. Return New MyBitmap(Image)
  25. End Operator
  26. Public Shared Widening Operator CType(Image As MyBitmap) As ImageSource
  27. If Image Is Nothing Then Return Nothing
  28. Dim Bitmap = Image.Pic
  29. Dim rect = New System.Drawing.Rectangle(0, 0, Bitmap.Width, Bitmap.Height)
  30. Dim bitmapData = Bitmap.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb)
  31. Try
  32. Dim size = rect.Width * rect.Height * 4
  33. Return BitmapSource.Create(Bitmap.Width, Bitmap.Height, Bitmap.HorizontalResolution, Bitmap.VerticalResolution, PixelFormats.Bgra32, Nothing, bitmapData.Scan0, size, bitmapData.Stride)
  34. Finally
  35. Bitmap.UnlockBits(bitmapData)
  36. End Try
  37. End Operator
  38. Public Shared Widening Operator CType(Image As System.Drawing.Bitmap) As MyBitmap
  39. If Image Is Nothing Then Return Nothing
  40. Return New MyBitmap(Image)
  41. End Operator
  42. Public Shared Widening Operator CType(Image As MyBitmap) As System.Drawing.Bitmap
  43. If Image Is Nothing Then Return Nothing
  44. Return Image.Pic
  45. End Operator
  46. Public Shared Widening Operator CType(Image As ImageBrush) As MyBitmap
  47. If Image Is Nothing Then Return Nothing
  48. Return New MyBitmap(Image)
  49. End Operator
  50. Public Shared Widening Operator CType(Image As MyBitmap) As ImageBrush
  51. If Image Is Nothing Then Return Nothing
  52. Return New ImageBrush(New MyBitmap(Image.Pic))
  53. End Operator
  54. '构造函数
  55. Public Sub New()
  56. End Sub
  57. Public Sub New(FilePathOrResourceName As String)
  58. Try
  59. FilePathOrResourceName = FilePathOrResourceName.Replace("pack://application:,,,/images/", PathImage)
  60. If FilePathOrResourceName.StartsWithF(PathImage) Then
  61. '使用缓存
  62. If BitmapCache.ContainsKey(FilePathOrResourceName) Then
  63. Pic = BitmapCache(FilePathOrResourceName).Pic
  64. Else
  65. Pic = New MyBitmap(CType((New ImageSourceConverter).ConvertFromString(FilePathOrResourceName), ImageSource))
  66. BitmapCache.TryAdd(FilePathOrResourceName, Pic)
  67. End If
  68. Else
  69. '使用这种自己接管 FileStream 的方法加载才能解除文件占用
  70. Using InputStream As New FileStream(FilePathOrResourceName, FileMode.Open)
  71. '判断是否为 WebP 文件头
  72. Dim Header(1) As Byte
  73. InputStream.Read(Header, 0, 2)
  74. InputStream.Seek(0, SeekOrigin.Begin)
  75. If Header(0) = 82 AndAlso Header(1) = 73 Then
  76. '读取 WebP
  77. Dim FileBytes(InputStream.Length - 1) As Byte
  78. InputStream.Read(FileBytes, 0, FileBytes.Length)
  79. Pic = WebPDecoder.DecodeFromBytes(FileBytes) '将代码隔离在另外一个类中,这样只要不走进这个分支就不会加载 Imazen.WebP.dll
  80. Else
  81. Pic = New System.Drawing.Bitmap(InputStream)
  82. End If
  83. End Using
  84. End If
  85. Catch ex As Exception
  86. Pic = My.Application.TryFindResource(FilePathOrResourceName)
  87. If Pic Is Nothing Then
  88. Pic = New System.Drawing.Bitmap(1, 1)
  89. Throw New Exception($"加载 MyBitmap 失败({FilePathOrResourceName})", ex)
  90. Else
  91. Log(ex, $"指定类型有误的 MyBitmap 加载({FilePathOrResourceName})", LogLevel.Developer)
  92. Exit Try
  93. End If
  94. End Try
  95. End Sub
  96. Public Sub New(Image As ImageSource)
  97. Using MS = New MemoryStream()
  98. Dim Encoder = New PngBitmapEncoder()
  99. Encoder.Frames.Add(BitmapFrame.Create(Image))
  100. Encoder.Save(MS)
  101. Pic = New System.Drawing.Bitmap(MS)
  102. End Using
  103. End Sub
  104. Public Sub New(Image As System.Drawing.Image)
  105. Pic = Image
  106. End Sub
  107. Public Sub New(Image As System.Drawing.Bitmap)
  108. Pic = Image
  109. End Sub
  110. Public Sub New(Image As ImageBrush)
  111. Using MS = New MemoryStream()
  112. Dim Encoder = New BmpBitmapEncoder()
  113. Encoder.Frames.Add(BitmapFrame.Create(Image.ImageSource))
  114. Encoder.Save(MS)
  115. Pic = New System.Drawing.Bitmap(MS)
  116. End Using
  117. End Sub
  118. Private Class WebPDecoder '将代码隔离在另外一个类中,这样只要不调用这个方法就不会加载 Imazen.WebP.dll
  119. Public Shared Function DecodeFromBytes(Bytes As Byte()) As System.Drawing.Bitmap
  120. If Is32BitSystem Then Throw New Exception("不支持在 32 位系统下加载 WebP 图片。")
  121. Dim Decoder As New Imazen.WebP.SimpleDecoder()
  122. Return Decoder.DecodeFromBytes(Bytes, Bytes.Length)
  123. End Function
  124. End Class
  125. ''' <summary>
  126. ''' 获取裁切的图片,这个方法不会导致原对象改变且会返回一个新的对象。
  127. ''' </summary>
  128. Public Function Clip(X As Integer, Y As Integer, Width As Integer, Height As Integer) As MyBitmap
  129. Dim bmp As New System.Drawing.Bitmap(Width, Height, Pic.PixelFormat)
  130. bmp.SetResolution(Pic.HorizontalResolution, Pic.VerticalResolution)
  131. Using g As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(bmp)
  132. g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor
  133. g.TranslateTransform(-X, -Y)
  134. g.DrawImage(Pic, New System.Drawing.Rectangle(0, 0, Pic.Width, Pic.Height))
  135. End Using
  136. Return bmp
  137. End Function
  138. ''' <summary>
  139. ''' 获取旋转或翻转后的图片,这个方法不会导致原对象改变且会返回一个新的对象。
  140. ''' </summary>
  141. Public Function RotateFlip(Type As System.Drawing.RotateFlipType) As MyBitmap
  142. Dim bmp As New System.Drawing.Bitmap(Pic)
  143. bmp.SetResolution(Pic.HorizontalResolution, Pic.VerticalResolution)
  144. bmp.RotateFlip(Type)
  145. Return bmp
  146. End Function
  147. ''' <summary>
  148. ''' 将图像保存到文件。
  149. ''' </summary>
  150. Public Sub Save(FilePath As String)
  151. Dim encoder As BitmapEncoder = New PngBitmapEncoder()
  152. encoder.Frames.Add(BitmapFrame.Create(Me))
  153. Using fileStream As New FileStream(FilePath, FileMode.Create)
  154. encoder.Save(fileStream)
  155. End Using
  156. End Sub
  157. End Class