视频播放器编程笔记,GDI DrawSting 显示不全(Graphics.VisibleClipBounds)

博客作者遇到在使用GDI进行字幕输出时,由于Graphics.VisibleClipBoundsProperty属性导致字幕显示不全的问题。原因是创建Graphics对象时该属性固定,无法适应控件大小变化。解决方法是在每次绘制前临时获取Graphics,确保正确显示。提醒开发者注意此属性的影响,避免类似陷阱。

在做字幕输出时,发现如果要想给字幕添加一个黑色的背景,让字幕更清楚些,只有自己画出字幕最方便。这就用到了 GDI。问题是:我输出的字幕不全,被齐刷刷的砍掉了一部分。后来发现是Graphics.VisibleClipBounds Property属性导致的显示不全。我用了成员变量 m_G 从构建函数中获得 控件的G,而这时这个属性就定死了!不能改了,这属性是只读的。因此后面控件大小发生变化,这个值并没有改变。导致输出不可见。引以为戒。大坑一个。

修改方法:每次画出之前,临时获得G,这样就可以了。
将代码改成如下形式:

    public void showSubtitle(string subText,bool bForce=false)
    { //draw the string with gdi for the shadow effect
        m_topTxtG = lblTopSubtitle.CreateGraphics();
        m_topTxtG.DrawString.....
     }

马拉孙 于 2021-03-07
北京泛五地区

Imports System.Drawing Imports System.Drawing.Drawing2D Imports System.Collections.Generic Imports JiuSpdfvUtl Imports JiuSpdfGeometry Imports System.IO ' GDI画布类 - 封装GDI+图形操作 Public Class GDICanvas Implements IDisposable Private _graphics As Graphics Private _bitmap As Bitmap Private _width As Integer Private _height As Integer Private _dpiX As Single Private _dpiY As Single Private _transformStack As New Stack(Of Matrix)() Private _stateStack As New Stack(Of GraphicsState)() Private _disposedValue As Boolean = False ' 构造函数 Public Sub New(width As Integer, height As Integer) _width = Math.Max(1, width) _height = Math.Max(1, height) Try ' 创建内存位图 _bitmap = New Bitmap(_width, _height) _graphics = Graphics.FromImage(_bitmap) InitializeGraphics() Catch ex As Exception LogHelper.LogError($"创建GDICanvas失败: {ex.Message}", "GDICanvas", ex) Throw New InvalidOperationException("无法创建图形画布", ex) End Try End Sub Public Sub New(graphics As Graphics, width As Integer, height As Integer) If graphics Is Nothing Then Throw New ArgumentNullException("graphics") End If _graphics = graphics _width = Math.Max(1, width) _height = Math.Max(1, height) _bitmap = Nothing ' 外部Graphics管理位图 InitializeGraphics() End Sub Public Sub New(image As Image) If image Is Nothing Then Throw New ArgumentNullException("image") End If _bitmap = If(TypeOf image Is Bitmap, DirectCast(image, Bitmap), New Bitmap(image)) _width = _bitmap.Width _height = _bitmap.Height _graphics = Graphics.FromImage(_bitmap) InitializeGraphics() End Sub ' 初始化图形设置 Private Sub InitializeGraphics() Try ' 设置高质量渲染 _graphics.SmoothingMode = SmoothingMode.AntiAlias _graphics.TextRenderingHint = Drawing.Text.TextRenderingHint.AntiAlias _graphics.InterpolationMode = InterpolationMode.HighQualityBicubic _graphics.PixelOffsetMode = PixelOffsetMode.HighQuality _graphics.CompositingQuality = CompositingQuality.HighQuality ' 获取DPI _dpiX = _graphics.DpiX _dpiY = _graphics.DpiY ' 保存原始状态 SaveState() Catch ex As Exception LogHelper.LogError($"初始化GDICanvas失败: {ex.Message}", "GDICanvas", ex) End Try End Sub ' 属性访问器 Public ReadOnly Property Graphics As Graphics Get Return _graphics End Get End Property Public ReadOnly Property Bitmap As Bitmap Get Return _bitmap End Get End Property Public ReadOnly Property Width As Integer Get Return _width End Get End Property Public ReadOnly Property Height As Integer Get Return _height End Get End Property Public ReadOnly Property DpiX As Single Get Return _dpiX End Get End Property Public ReadOnly Property DpiY As Single Get Return _dpiY End Get End Property Public ReadOnly Property Size As Size Get Return New Size(_width, _height) End Get End Property Public ReadOnly Property Bounds As Rectangle Get Return New Rectangle(0, 0, _width, _height) End Get End Property Public ReadOnly Property IsDisposed As Boolean Get Return _disposedValue OrElse _graphics Is Nothing End Get End Property Public ReadOnly Property HasBitmap As Boolean Get Return _bitmap IsNot Nothing End Get End Property ' 状态管理 Public Sub SaveState() If IsDisposed Then Return Try _stateStack.Push(_graphics.Save()) Catch ex As Exception LogHelper.LogError($"保存图形状态失败: {ex.Message}", "GDICanvas") End Try End Sub Public Sub RestoreState() If IsDisposed OrElse _stateStack.Count = 0 Then Return Try _graphics.Restore(_stateStack.Pop()) Catch ex As Exception LogHelper.LogError($"恢复图形状态失败: {ex.Message}", "GDICanvas") End Try End Sub ' 变换管理 Public Sub PushTransform() If IsDisposed Then Return Try _transformStack.Push(_graphics.Transform.Clone()) Catch ex As Exception LogHelper.LogError($"保存变换失败: {ex.Message}", "GDICanvas") End Try End Sub Public Sub PopTransform() If IsDisposed OrElse _transformStack.Count = 0 Then Return Try _graphics.Transform = _transformStack.Pop() Catch ex As Exception LogHelper.LogError($"恢复变换失败: {ex.Message}", "GDICanvas") End Try End Sub ' 应用PDF矩阵变换 Public Sub ApplyMatrix(matrix As PDFMatrix) If IsDisposed OrElse matrix Is Nothing Then Return Try Dim gdiMatrix = matrix.ToMatrix() _graphics.MultiplyTransform(gdiMatrix) Catch ex As Exception LogHelper.LogError($"应用矩阵变换失败: {ex.Message}", "GDICanvas") End Try End Sub Public Sub ApplyMatrix(a As Single, b As Single, c As Single, d As Single, e As Single, f As Single) ApplyMatrix(New PDFMatrix(New Single() {a, b, c, d, e, f})) End Sub ' 重置变换 Public Sub ResetTransform() If IsDisposed Then Return Try _graphics.ResetTransform() Catch ex As Exception LogHelper.LogError($"重置变换失败: {ex.Message}", "GDICanvas") End Try End Sub ' 坐标系转换 Public Function ToCanvasPoint(pdfPoint As PDFPoint) As PointF If IsDisposed OrElse pdfPoint Is Nothing Then Return PointF.Empty Try Dim matrix = _graphics.Transform Dim points As PointF() = {pdfPoint.ToPointF()} matrix.TransformPoints(points) Return points(0) Catch ex As Exception LogHelper.LogError($"坐标转换失败: {ex.Message}", "GDICanvas") Return pdfPoint.ToPointF() End Try End Function Public Function ToCanvasRectangle(pdfRect As PDFRectangle) As RectangleF If IsDisposed OrElse pdfRect Is Nothing Then Return RectangleF.Empty Try Dim points As PointF() = { New PointF(pdfRect.Left, pdfRect.Top), New PointF(pdfRect.Right, pdfRect.Bottom) } Dim matrix = _graphics.Transform matrix.TransformPoints(points) Dim x = Math.Min(points(0).X, points(1).X) Dim y = Math.Min(points(0).Y, points(1).Y) Dim width = Math.Abs(points(1).X - points(0).X) Dim height = Math.Abs(points(1).Y - points(0).Y) Return New RectangleF(x, y, width, height) Catch ex As Exception LogHelper.LogError($"矩形转换失败: {ex.Message}", "GDICanvas") Return pdfRect.ToRectangleF() End Try End Function Public Function ToPDFPoint(canvasPoint As PointF) As PDFPoint If IsDisposed Then Return PDFPoint.Empty Try Dim matrix = _graphics.Transform Dim inverseMatrix = matrix.Clone() inverseMatrix.Invert() Dim points As PointF() = {canvasPoint} inverseMatrix.TransformPoints(points) Return New PDFPoint(points(0)) Catch ex As Exception LogHelper.LogError($"逆向坐标转换失败: {ex.Message}", "GDICanvas") Return New PDFPoint(canvasPoint.X, canvasPoint.Y) End Try End Function ' 清除画布 Public Sub Clear(color As Color) If IsDisposed Then Return Try ' 保存当前状态 SaveState() ' 重置变换以清除整个区域 Dim savedTransform = _graphics.Transform _graphics.ResetTransform() _graphics.Clear(color) _graphics.Transform = savedTransform ' 恢复状态 RestoreState() Catch ex As Exception LogHelper.LogError($"清除画布失败: {ex.Message}", "GDICanvas") End Try End Sub Public Sub Clear() Clear(Color.White) End Sub ' 裁剪区域 Public Sub SetClip(rect As PDFRectangle) If IsDisposed OrElse rect Is Nothing Then Return Try Dim canvasRect = ToCanvasRectangle(rect) _graphics.SetClip(canvasRect) Catch ex As Exception LogHelper.LogError($"设置裁剪区域失败: {ex.Message}", "GDICanvas") End Try End Sub Public Sub SetClip(path As PDFPath) If IsDisposed OrElse path Is Nothing Then Return Try Dim graphicsPath = path.ToGraphicsPath() _graphics.SetClip(graphicsPath) Catch ex As Exception LogHelper.LogError($"设置路径裁剪失败: {ex.Message}", "GDICanvas") End Try End Sub Public Sub SetClip(region As Region) If IsDisposed OrElse region Is Nothing Then Return Try _graphics.SetClip(region) Catch ex As Exception LogHelper.LogError($"设置区域裁剪失败: {ex.Message}", "GDICanvas") End Try End Sub Public Sub ResetClip() If IsDisposed Then Return Try _graphics.ResetClip() Catch ex As Exception LogHelper.LogError($"重置裁剪失败: {ex.Message}", "GDICanvas") End Try End Sub ' 获取可见区域边界 Public ReadOnly Property VisibleClipBounds As RectangleF Get If IsDisposed Then Return RectangleF.Empty Return _graphics.VisibleClipBounds End Get End Property ' 检查可见性 Public Function IsPointVisible(point As PDFPoint) As Boolean If IsDisposed OrElse point Is Nothing Then Return False Try Dim canvasPoint = ToCanvasPoint(point) Return VisibleClipBounds.Contains(canvasPoint) Catch ex As Exception Return False End Try End Function Public Function IsRectangleVisible(rect As PDFRectangle) As Boolean If IsDisposed OrElse rect Is Nothing Then Return False Try Dim canvasRect = ToCanvasRectangle(rect) Return VisibleClipBounds.IntersectsWith(canvasRect) Catch ex As Exception Return False End Try End Function ' 渲染到另一个Graphics Public Sub RenderTo(targetGraphics As Graphics, destRect As Rectangle) If IsDisposed OrElse targetGraphics Is Nothing Then Return Try If _bitmap IsNot Nothing Then targetGraphics.DrawImage(_bitmap, destRect) End If Catch ex As Exception LogHelper.LogError($"渲染到Graphics失败: {ex.Message}", "GDICanvas") End Try End Sub Public Sub RenderTo(targetGraphics As Graphics, destPoint As Point) If IsDisposed OrElse targetGraphics Is Nothing Then Return Try If _bitmap IsNot Nothing Then targetGraphics.DrawImageUnscaled(_bitmap, destPoint) End If Catch ex As Exception LogHelper.LogError($"渲染到Graphics失败: {ex.Message}", "GDICanvas") End Try End Sub ' 保存到文件 Public Sub SaveToFile(filePath As String, Optional format As Imaging.ImageFormat = Nothing) If IsDisposed OrElse _bitmap Is Nothing Then Return Try If format Is Nothing Then format = Imaging.ImageFormat.Png End If _bitmap.Save(filePath, format) Catch ex As Exception LogHelper.LogError($"保存到文件失败: {ex.Message}", "GDICanvas", ex) Throw New IOException($"无法保存图像到文件: {filePath}", ex) End Try End Sub ' 获取图像数据 Public Function GetImageBytes(Optional format As Imaging.ImageFormat = Nothing) As Byte() If IsDisposed OrElse _bitmap Is Nothing Then Return New Byte() {} Try Using stream As New IO.MemoryStream() If format Is Nothing Then format = Imaging.ImageFormat.Png End If _bitmap.Save(stream, format) Return stream.ToArray() End Using Catch ex As Exception LogHelper.LogError($"获取图像数据失败: {ex.Message}", "GDICanvas", ex) Return New Byte() {} End Try End Function ' 调整大小 Public Function Resize(newWidth As Integer, newHeight As Integer) As GDICanvas If IsDisposed Then Return Nothing Try Dim resizedCanvas As New GDICanvas(newWidth, newHeight) resizedCanvas.Graphics.DrawImage(_bitmap, 0, 0, newWidth, newHeight) Return resizedCanvas Catch ex As Exception LogHelper.LogError($"调整画布大小失败: {ex.Message}", "GDICanvas", ex) Return Nothing End Try End Function ' 创建副本 Public Function Clone() As GDICanvas If IsDisposed Then Return Nothing Try If _bitmap IsNot Nothing Then Return New GDICanvas(_bitmap.Clone()) Else Return New GDICanvas(_width, _height) End If Catch ex As Exception LogHelper.LogError($"克隆画布失败: {ex.Message}", "GDICanvas", ex) Return Nothing End Try End Function ' 测量文本 Public Function MeasureText(text As String, font As Font) As SizeF If IsDisposed OrElse text Is Nothing OrElse font Is Nothing Then Return SizeF.Empty End If Try Return _graphics.MeasureString(text, font) Catch ex As Exception LogHelper.LogError($"测量文本失败: {ex.Message}", "GDICanvas") Return SizeF.Empty End Try End Function Public Function MeasureText(text As String, font As Font, maxWidth As Single) As SizeF If IsDisposed OrElse text Is Nothing OrElse font Is Nothing Then Return SizeF.Empty End If Try Return _graphics.MeasureString(text, font, CInt(maxWidth)) Catch ex As Exception LogHelper.LogError($"测量文本失败: {ex.Message}", "GDICanvas") Return SizeF.Empty End Try End Function ' 检查是否支持透明 Public ReadOnly Property SupportsTransparency As Boolean Get If _bitmap Is Nothing Then Return False Return _bitmap.PixelFormat = Imaging.PixelFormat.Format32bppArgb End Get End Property ' 设置透明度支持 Public Sub EnableTransparency() If IsDisposed OrElse _bitmap Is Nothing Then Return Try If Not SupportsTransparency Then Dim newBitmap As New Bitmap(_width, _height, Imaging.PixelFormat.Format32bppArgb) Using g = Graphics.FromImage(newBitmap) g.DrawImage(_bitmap, Point.Empty) End Using _bitmap.Dispose() _bitmap = newBitmap _graphics.Dispose() _graphics = Graphics.FromImage(_bitmap) InitializeGraphics() End If Catch ex As Exception LogHelper.LogError($"启用透明度失败: {ex.Message}", "GDICanvas") End Try End Sub ' 获取像素颜色 Public Function GetPixel(x As Integer, y As Integer) As Color If IsDisposed OrElse _bitmap Is Nothing Then Return Color.Empty Try If x >= 0 AndAlso x < _width AndAlso y >= 0 AndAlso y < _height Then Return _bitmap.GetPixel(x, y) End If Return Color.Empty Catch ex As Exception LogHelper.LogError($"获取像素颜色失败: {ex.Message}", "GDICanvas") Return Color.Empty End Try End Function ' 设置像素颜色 Public Sub SetPixel(x As Integer, y As Integer, color As Color) If IsDisposed OrElse _bitmap Is Nothing Then Return Try If x >= 0 AndAlso x < _width AndAlso y >= 0 AndAlso y < _height Then _bitmap.SetPixel(x, y, color) End If Catch ex As Exception LogHelper.LogError($"设置像素颜色失败: {ex.Message}", "GDICanvas") End Try End Sub ' 释放资源 Protected Overridable Sub Dispose(disposing As Boolean) If Not _disposedValue Then If disposing Then If _graphics IsNot Nothing Then _graphics.Dispose() _graphics = Nothing End If If _bitmap IsNot Nothing Then _bitmap.Dispose() _bitmap = Nothing End If While _stateStack.Count > 0 _stateStack.Pop() End While While _transformStack.Count > 0 _transformStack.Pop().Dispose() End While End If _disposedValue = True End If End Sub Public Sub Dispose() Implements IDisposable.Dispose Dispose(disposing:=True) GC.SuppressFinalize(Me) End Sub Protected Overrides Sub Finalize() Dispose(disposing:=False) End Sub End Class
最新发布
12-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值