using AngleSharp.Dom; using XFESystem.Desktop; using XFESystem.Browser; namespace XfePaintBaseline; public static class BaselinePagePainter { public static void Paint(IDesktopCanvas canvas, DesktopBounds viewport, BrowserDisplayList list, Func? surfaces = null) { var web = canvas as IDesktopWebCanvas; var rounded = new BaselineClipCanvas(canvas); var clips = new Stack(); var clip = new BrowserRect(viewport.X, viewport.Y, viewport.Width, viewport.Height); web?.PushClip(viewport); canvas.Fill(0xFFFFFF, viewport.X, viewport.Y, viewport.Width, viewport.Height); try { foreach (var item in list.Commands) { if (item.Opacity <= 0 && item.Kind is not (BrowserDrawKind.PushClip or BrowserDrawKind.PopClip)) continue; var r = item.Bounds.Translate(viewport.X - list.Viewport.ScrollX, viewport.Y - list.Viewport.ScrollY); if (item.Kind == BrowserDrawKind.PushClip) { clips.Push(clip); clip = clip.Intersect(r); rounded.Push(item.RoundedClip,viewport.X-list.Viewport.ScrollX,viewport.Y-list.Viewport.ScrollY); web?.PushClip(ToBounds(clip)); continue; } if (item.Kind == BrowserDrawKind.PopClip) { if (clips.TryPop(out var old)) { clip = old;rounded.Pop(); web?.PopClip(); } continue; } var visible = r.Intersect(clip); if (visible.Width <= 0 || visible.Height <= 0) continue; IDesktopCanvas target=rounded.Active&&canvas is IDesktopCoverageCanvas?rounded:canvas; if(item.Decoration is not null) BaselineBoxPainter.Paint(target,item,r,visible); else if (item.Kind == BrowserDrawKind.Canvas) { if (item.Node is not null && surfaces?.Invoke(item.Node) is { } surface) PaintCanvas(target, r, visible, surface, item.Opacity); } else if (item.Kind == BrowserDrawKind.Text) { double leading=web is null?0:(r.Height-web.MeasureWebLineHeight(item.Font.Size,item.Font.Family,item.Font.Weight))/2; if(web is not null&&rounded.Active&&canvas is IDesktopCoverageCanvas)rounded.WebText(web,item,r.Translate(0,leading)); else if (web is not null) web.WebText(item.Text, item.Color, r.X, r.Y+leading, item.Font.Size, item.Font.Family, item.Font.Weight, item.Opacity); else if (visible == r) canvas.Text(item.Text, item.Color, (int)r.X, (int)r.Y); } else { var b = ToBounds(visible); if (item.Opacity < 1 && target is IDesktopCoverageCanvas coverage) { int left = (int)Math.Floor(visible.X * canvas.ScalePercent / 100d), top = (int)Math.Floor(visible.Y * canvas.ScalePercent / 100d); int width = Math.Max(1, (int)Math.Ceiling(visible.Width * canvas.ScalePercent / 100d)), height = Math.Max(1, (int)Math.Ceiling(visible.Height * canvas.ScalePercent / 100d)); var row = new byte[width]; Array.Fill(row, (byte)Math.Clamp(item.Opacity * 255, 0, 255)); for (int y = 0; y < height; y++) coverage.BlendCoverage(item.Color, left, top + y, width, 1, row); } else target.Fill(item.Color, b.X, b.Y, b.Width, b.Height); } } } catch { while (clips.TryPop(out _)) web?.PopClip(); web?.PopClip(); throw; } while (clips.TryPop(out _)) web?.PopClip(); web?.PopClip(); } private static void PaintCanvas(IDesktopCanvas canvas, BrowserRect bounds, BrowserRect visible, BrowserCanvasSurface surface, double opacity) { if (surface.Width <= 0 || surface.Height <= 0 || bounds.Width <= 0 || bounds.Height <= 0) return; var pixels = surface.Pixels.Span; int left = (int)Math.Floor(visible.Left), right = (int)Math.Ceiling(visible.Right), top = (int)Math.Floor(visible.Top), bottom = (int)Math.Ceiling(visible.Bottom); for (int row = top; row < bottom; row++) { int sourceY = Math.Clamp((int)((row - bounds.Y) * surface.Height / bounds.Height), 0, surface.Height - 1); for (int x = left; x < right;) { int sourceX = Math.Clamp((int)((x - bounds.X) * surface.Width / bounds.Width), 0, surface.Width - 1); uint color = pixels[sourceY * surface.Width + sourceX]; int end = x + 1; while (end < right && pixels[sourceY * surface.Width + Math.Clamp((int)((end - bounds.X) * surface.Width / bounds.Width), 0, surface.Width - 1)] == color) end++; int alpha = (int)((color >> 24) * opacity); if (alpha >= 255) canvas.Fill(color & 0xFFFFFF, x, row, end - x, 1); else if (alpha > 0 && canvas is IDesktopCoverageCanvas coverage) { int nx = (int)Math.Round(x * canvas.ScalePercent / 100d), ny = (int)Math.Round(row * canvas.ScalePercent / 100d); int nw = Math.Max(1,(int)Math.Round(end * canvas.ScalePercent / 100d)-nx), nh = Math.Max(1,(int)Math.Round((row+1) * canvas.ScalePercent / 100d)-ny); var mask = new byte[nw]; Array.Fill(mask,(byte)alpha); for (int sy = 0; sy < nh; sy++) coverage.BlendCoverage(color & 0xFFFFFF, nx, ny + sy, nw,1,mask); } x = end; } } } private static DesktopBounds ToBounds(BrowserRect r) => new((int)Math.Floor(r.X), (int)Math.Floor(r.Y), Math.Max(0, (int)Math.Ceiling(r.Right) - (int)Math.Floor(r.X)), Math.Max(0, (int)Math.Ceiling(r.Bottom) - (int)Math.Floor(r.Y))); }