------ #### RaUIV4 V4.0.2311.0701 - *.[全新]整合了MyDb、ryControls、MyDb_MySQL等dll文件到RaUI一个项目。 - *.[新增]新增ApkOp类,可以轻松获取APK信息。 - *.[新增]新增JsonExt扩展类,让Json操作更简单。 - *.[新增]新增WebP类,可以支持webp格式的图片。 - *.[改进]ryQuickSQL中的AddField方法改为自动替换已存在的同名值。 - *.[修复]ryQuickSQL中的AddFieldCalc方法无法正常计算的BUG。
467 lines
15 KiB
C#
467 lines
15 KiB
C#
// "Therefore those skilled at the unorthodox
|
|
// are infinite as heaven and earth,
|
|
// inexhaustible as the great rivers.
|
|
// When they come to an end,
|
|
// they begin again,
|
|
// like the days and months;
|
|
// they die and are reborn,
|
|
// like the four seasons."
|
|
//
|
|
// - Sun Tsu,
|
|
// "The Art of War"
|
|
|
|
using System;
|
|
using System.Drawing;
|
|
using System.Drawing.Drawing2D;
|
|
using TheArtOfDev.HtmlRenderer.Adapters.Entities;
|
|
using TheArtOfDev.HtmlRenderer.Core.Utils;
|
|
using TheArtOfDev.HtmlRenderer.Adapters;
|
|
using TheArtOfDev.HtmlRenderer.WinForms.Utilities;
|
|
|
|
namespace TheArtOfDev.HtmlRenderer.WinForms.Adapters
|
|
{
|
|
/// <summary>
|
|
/// Adapter for WinForms Graphics for core.
|
|
/// </summary>
|
|
internal sealed class GraphicsAdapter : RGraphics
|
|
{
|
|
#region Fields and Consts
|
|
|
|
/// <summary>
|
|
/// used for <see cref="MeasureString(string,RFont,double,out int,out double)"/> calculation.
|
|
/// </summary>
|
|
private static readonly int[] _charFit = new int[1];
|
|
|
|
/// <summary>
|
|
/// used for <see cref="MeasureString(string,RFont,double,out int,out double)"/> calculation.
|
|
/// </summary>
|
|
private static readonly int[] _charFitWidth = new int[1000];
|
|
|
|
/// <summary>
|
|
/// Used for GDI+ measure string.
|
|
/// </summary>
|
|
private static readonly CharacterRange[] _characterRanges = new CharacterRange[1];
|
|
|
|
/// <summary>
|
|
/// The string format to use for measuring strings for GDI+ text rendering
|
|
/// </summary>
|
|
private static readonly StringFormat _stringFormat;
|
|
|
|
/// <summary>
|
|
/// The string format to use for rendering strings for GDI+ text rendering
|
|
/// </summary>
|
|
private static readonly StringFormat _stringFormat2;
|
|
|
|
/// <summary>
|
|
/// The wrapped WinForms graphics object
|
|
/// </summary>
|
|
private readonly Graphics _g;
|
|
|
|
/// <summary>
|
|
/// Use GDI+ text rendering to measure/draw text.
|
|
/// </summary>
|
|
private readonly bool _useGdiPlusTextRendering;
|
|
|
|
#if !MONO
|
|
/// <summary>
|
|
/// the initialized HDC used
|
|
/// </summary>
|
|
private IntPtr _hdc;
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// if to release the graphics object on dispose
|
|
/// </summary>
|
|
private readonly bool _releaseGraphics;
|
|
|
|
/// <summary>
|
|
/// If text alignment was set to RTL
|
|
/// </summary>
|
|
private bool _setRtl;
|
|
|
|
#endregion
|
|
|
|
|
|
/// <summary>
|
|
/// Init static resources.
|
|
/// </summary>
|
|
static GraphicsAdapter()
|
|
{
|
|
_stringFormat = new StringFormat(StringFormat.GenericTypographic);
|
|
_stringFormat.FormatFlags = StringFormatFlags.NoClip | StringFormatFlags.MeasureTrailingSpaces;
|
|
|
|
_stringFormat2 = new StringFormat(StringFormat.GenericTypographic);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Init.
|
|
/// </summary>
|
|
/// <param name="g">the win forms graphics object to use</param>
|
|
/// <param name="useGdiPlusTextRendering">Use GDI+ text rendering to measure/draw text</param>
|
|
/// <param name="releaseGraphics">optional: if to release the graphics object on dispose (default - false)</param>
|
|
public GraphicsAdapter(Graphics g, bool useGdiPlusTextRendering, bool releaseGraphics = false)
|
|
: base(WinFormsAdapter.Instance, Utils.Convert(g.ClipBounds))
|
|
{
|
|
ArgChecker.AssertArgNotNull(g, "g");
|
|
|
|
_g = g;
|
|
_releaseGraphics = releaseGraphics;
|
|
|
|
#if MONO
|
|
_useGdiPlusTextRendering = true;
|
|
#else
|
|
_useGdiPlusTextRendering = useGdiPlusTextRendering;
|
|
#endif
|
|
}
|
|
|
|
public override void PopClip()
|
|
{
|
|
ReleaseHdc();
|
|
_clipStack.Pop();
|
|
_g.SetClip(Utils.Convert(_clipStack.Peek()), CombineMode.Replace);
|
|
}
|
|
|
|
public override void PushClip(RRect rect)
|
|
{
|
|
ReleaseHdc();
|
|
_clipStack.Push(rect);
|
|
_g.SetClip(Utils.Convert(rect), CombineMode.Replace);
|
|
}
|
|
|
|
public override void PushClipExclude(RRect rect)
|
|
{
|
|
ReleaseHdc();
|
|
_clipStack.Push(_clipStack.Peek());
|
|
_g.SetClip(Utils.Convert(rect), CombineMode.Exclude);
|
|
}
|
|
|
|
public override Object SetAntiAliasSmoothingMode()
|
|
{
|
|
ReleaseHdc();
|
|
var prevMode = _g.SmoothingMode;
|
|
_g.SmoothingMode = SmoothingMode.AntiAlias;
|
|
return prevMode;
|
|
}
|
|
|
|
public override void ReturnPreviousSmoothingMode(Object prevMode)
|
|
{
|
|
if (prevMode != null)
|
|
{
|
|
ReleaseHdc();
|
|
_g.SmoothingMode = (SmoothingMode)prevMode;
|
|
}
|
|
}
|
|
|
|
public override RSize MeasureString(string str, RFont font)
|
|
{
|
|
if (_useGdiPlusTextRendering)
|
|
{
|
|
ReleaseHdc();
|
|
var fontAdapter = (FontAdapter)font;
|
|
var realFont = fontAdapter.Font;
|
|
_characterRanges[0] = new CharacterRange(0, str.Length);
|
|
_stringFormat.SetMeasurableCharacterRanges(_characterRanges);
|
|
var size = _g.MeasureCharacterRanges(str, realFont, RectangleF.Empty, _stringFormat)[0].GetBounds(_g).Size;
|
|
|
|
if (font.Height < 0)
|
|
{
|
|
var height = realFont.Height;
|
|
var descent = realFont.Size * realFont.FontFamily.GetCellDescent(realFont.Style) / realFont.FontFamily.GetEmHeight(realFont.Style);
|
|
fontAdapter.SetMetrics(height, (int)Math.Round((height - descent + .5f)));
|
|
}
|
|
|
|
return Utils.Convert(size);
|
|
}
|
|
else
|
|
{
|
|
#if !MONO
|
|
SetFont(font);
|
|
var size = new Size();
|
|
Win32Utils.GetTextExtentPoint32(_hdc, str, str.Length, ref size);
|
|
|
|
if (font.Height < 0)
|
|
{
|
|
TextMetric lptm;
|
|
Win32Utils.GetTextMetrics(_hdc, out lptm);
|
|
((FontAdapter)font).SetMetrics(size.Height, lptm.tmHeight - lptm.tmDescent + lptm.tmUnderlined + 1);
|
|
}
|
|
|
|
return Utils.Convert(size);
|
|
#else
|
|
throw new InvalidProgramException("Invalid Mono code");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
public override void MeasureString(string str, RFont font, double maxWidth, out int charFit, out double charFitWidth)
|
|
{
|
|
charFit = 0;
|
|
charFitWidth = 0;
|
|
if (_useGdiPlusTextRendering)
|
|
{
|
|
ReleaseHdc();
|
|
|
|
var size = MeasureString(str, font);
|
|
|
|
for (int i = 1; i <= str.Length; i++)
|
|
{
|
|
charFit = i - 1;
|
|
RSize pSize = MeasureString(str.Substring(0, i), font);
|
|
if (pSize.Height <= size.Height && pSize.Width < maxWidth)
|
|
charFitWidth = pSize.Width;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if !MONO
|
|
SetFont(font);
|
|
|
|
var size = new Size();
|
|
Win32Utils.GetTextExtentExPoint(_hdc, str, str.Length, (int)Math.Round(maxWidth), _charFit, _charFitWidth, ref size);
|
|
charFit = _charFit[0];
|
|
charFitWidth = charFit > 0 ? _charFitWidth[charFit - 1] : 0;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
public override void DrawString(string str, RFont font, RColor color, RPoint point, RSize size, bool rtl)
|
|
{
|
|
if (_useGdiPlusTextRendering)
|
|
{
|
|
ReleaseHdc();
|
|
SetRtlAlignGdiPlus(rtl);
|
|
var brush = ((BrushAdapter)_adapter.GetSolidBrush(color)).Brush;
|
|
_g.DrawString(str, ((FontAdapter)font).Font, brush, (int)(Math.Round(point.X) + (rtl ? size.Width : 0)), (int)Math.Round(point.Y), _stringFormat2);
|
|
}
|
|
else
|
|
{
|
|
#if !MONO
|
|
var pointConv = Utils.ConvertRound(point);
|
|
var colorConv = Utils.Convert(color);
|
|
|
|
if (color.A == 255)
|
|
{
|
|
SetFont(font);
|
|
SetTextColor(colorConv);
|
|
SetRtlAlignGdi(rtl);
|
|
|
|
Win32Utils.TextOut(_hdc, pointConv.X, pointConv.Y, str, str.Length);
|
|
}
|
|
else
|
|
{
|
|
InitHdc();
|
|
SetRtlAlignGdi(rtl);
|
|
DrawTransparentText(_hdc, str, font, pointConv, Utils.ConvertRound(size), colorConv);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
public override RBrush GetTextureBrush(RImage image, RRect dstRect, RPoint translateTransformLocation)
|
|
{
|
|
var brush = new TextureBrush(((ImageAdapter)image).Image, Utils.Convert(dstRect));
|
|
brush.TranslateTransform((float)translateTransformLocation.X, (float)translateTransformLocation.Y);
|
|
return new BrushAdapter(brush, true);
|
|
}
|
|
|
|
public override RGraphicsPath GetGraphicsPath()
|
|
{
|
|
return new GraphicsPathAdapter();
|
|
}
|
|
|
|
public override void Dispose()
|
|
{
|
|
ReleaseHdc();
|
|
if (_releaseGraphics)
|
|
_g.Dispose();
|
|
|
|
if (_useGdiPlusTextRendering && _setRtl)
|
|
_stringFormat2.FormatFlags ^= StringFormatFlags.DirectionRightToLeft;
|
|
}
|
|
|
|
|
|
#region Delegate graphics methods
|
|
|
|
public override void DrawLine(RPen pen, double x1, double y1, double x2, double y2)
|
|
{
|
|
ReleaseHdc();
|
|
_g.DrawLine(((PenAdapter)pen).Pen, (float)x1, (float)y1, (float)x2, (float)y2);
|
|
}
|
|
|
|
public override void DrawRectangle(RPen pen, double x, double y, double width, double height)
|
|
{
|
|
ReleaseHdc();
|
|
_g.DrawRectangle(((PenAdapter)pen).Pen, (float)x, (float)y, (float)width, (float)height);
|
|
}
|
|
|
|
public override void DrawRectangle(RBrush brush, double x, double y, double width, double height)
|
|
{
|
|
ReleaseHdc();
|
|
_g.FillRectangle(((BrushAdapter)brush).Brush, (float)x, (float)y, (float)width, (float)height);
|
|
}
|
|
|
|
public override void DrawImage(RImage image, RRect destRect, RRect srcRect)
|
|
{
|
|
ReleaseHdc();
|
|
_g.DrawImage(((ImageAdapter)image).Image, Utils.Convert(destRect), Utils.Convert(srcRect), GraphicsUnit.Pixel);
|
|
}
|
|
|
|
public override void DrawImage(RImage image, RRect destRect)
|
|
{
|
|
ReleaseHdc();
|
|
_g.DrawImage(((ImageAdapter)image).Image, Utils.Convert(destRect));
|
|
}
|
|
|
|
public override void DrawPath(RPen pen, RGraphicsPath path)
|
|
{
|
|
_g.DrawPath(((PenAdapter)pen).Pen, ((GraphicsPathAdapter)path).GraphicsPath);
|
|
}
|
|
|
|
public override void DrawPath(RBrush brush, RGraphicsPath path)
|
|
{
|
|
ReleaseHdc();
|
|
_g.FillPath(((BrushAdapter)brush).Brush, ((GraphicsPathAdapter)path).GraphicsPath);
|
|
}
|
|
|
|
public override void DrawPolygon(RBrush brush, RPoint[] points)
|
|
{
|
|
if (points != null && points.Length > 0)
|
|
{
|
|
ReleaseHdc();
|
|
_g.FillPolygon(((BrushAdapter)brush).Brush, Utils.Convert(points));
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
#region Private methods
|
|
|
|
/// <summary>
|
|
/// Release current HDC to be able to use <see cref="Graphics"/> methods.
|
|
/// </summary>
|
|
private void ReleaseHdc()
|
|
{
|
|
#if !MONO
|
|
if (_hdc != IntPtr.Zero)
|
|
{
|
|
Win32Utils.SelectClipRgn(_hdc, IntPtr.Zero);
|
|
_g.ReleaseHdc(_hdc);
|
|
_hdc = IntPtr.Zero;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if !MONO
|
|
/// <summary>
|
|
/// Init HDC for the current graphics object to be used to call GDI directly.
|
|
/// </summary>
|
|
private void InitHdc()
|
|
{
|
|
if (_hdc == IntPtr.Zero)
|
|
{
|
|
var clip = _g.Clip.GetHrgn(_g);
|
|
|
|
_hdc = _g.GetHdc();
|
|
_setRtl = false;
|
|
Win32Utils.SetBkMode(_hdc, 1);
|
|
|
|
Win32Utils.SelectClipRgn(_hdc, clip);
|
|
|
|
Win32Utils.DeleteObject(clip);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set a resource (e.g. a font) for the specified device context.
|
|
/// WARNING: Calling Font.ToHfont() many times without releasing the font handle crashes the app.
|
|
/// </summary>
|
|
private void SetFont(RFont font)
|
|
{
|
|
InitHdc();
|
|
Win32Utils.SelectObject(_hdc, ((FontAdapter)font).HFont);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the text color of the device context.
|
|
/// </summary>
|
|
private void SetTextColor(Color color)
|
|
{
|
|
InitHdc();
|
|
int rgb = (color.B & 0xFF) << 16 | (color.G & 0xFF) << 8 | color.R;
|
|
Win32Utils.SetTextColor(_hdc, rgb);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Change text align to Left-to-Right or Right-to-Left if required.
|
|
/// </summary>
|
|
private void SetRtlAlignGdi(bool rtl)
|
|
{
|
|
if (_setRtl)
|
|
{
|
|
if (!rtl)
|
|
Win32Utils.SetTextAlign(_hdc, Win32Utils.TextAlignDefault);
|
|
}
|
|
else if (rtl)
|
|
{
|
|
Win32Utils.SetTextAlign(_hdc, Win32Utils.TextAlignRtl);
|
|
}
|
|
_setRtl = rtl;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Special draw logic to draw transparent text using GDI.<br/>
|
|
/// 1. Create in-memory DC<br/>
|
|
/// 2. Copy background to in-memory DC<br/>
|
|
/// 3. Draw the text to in-memory DC<br/>
|
|
/// 4. Copy the in-memory DC to the proper location with alpha blend<br/>
|
|
/// </summary>
|
|
private static void DrawTransparentText(IntPtr hdc, string str, RFont font, Point point, Size size, Color color)
|
|
{
|
|
IntPtr dib;
|
|
var memoryHdc = Win32Utils.CreateMemoryHdc(hdc, size.Width, size.Height, out dib);
|
|
|
|
try
|
|
{
|
|
// copy target background to memory HDC so when copied back it will have the proper background
|
|
Win32Utils.BitBlt(memoryHdc, 0, 0, size.Width, size.Height, hdc, point.X, point.Y, Win32Utils.BitBltCopy);
|
|
|
|
// Create and select font
|
|
Win32Utils.SelectObject(memoryHdc, ((FontAdapter)font).HFont);
|
|
Win32Utils.SetTextColor(memoryHdc, (color.B & 0xFF) << 16 | (color.G & 0xFF) << 8 | color.R);
|
|
|
|
// Draw text to memory HDC
|
|
Win32Utils.TextOut(memoryHdc, 0, 0, str, str.Length);
|
|
|
|
// copy from memory HDC to normal HDC with alpha blend so achieve the transparent text
|
|
Win32Utils.AlphaBlend(hdc, point.X, point.Y, size.Width, size.Height, memoryHdc, 0, 0, size.Width, size.Height, new BlendFunction(color.A));
|
|
}
|
|
finally
|
|
{
|
|
Win32Utils.ReleaseMemoryHdc(memoryHdc, dib);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// Change text align to Left-to-Right or Right-to-Left if required.
|
|
/// </summary>
|
|
private void SetRtlAlignGdiPlus(bool rtl)
|
|
{
|
|
if (_setRtl)
|
|
{
|
|
if (!rtl)
|
|
_stringFormat2.FormatFlags ^= StringFormatFlags.DirectionRightToLeft;
|
|
}
|
|
else if (rtl)
|
|
{
|
|
_stringFormat2.FormatFlags |= StringFormatFlags.DirectionRightToLeft;
|
|
}
|
|
_setRtl = rtl;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |