XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
namespace XFESystem.Branding;

/// <summary>Allocation-free, integer-only boot artwork. No fonts, text, OS or runtime APIs.</summary>
public static unsafe class BootVisual
{
    public const int IntroMilliseconds = 2400;
    public const uint Cyan = 0x20C9FF;
    public static bool ShouldRelease(ulong elapsedMilliseconds, bool applicationReady) =>
        applicationReady && elapsedMilliseconds >= IntroMilliseconds;
    public static bool IsWaiting(ulong elapsedMilliseconds, bool applicationReady) =>
        !applicationReady && elapsedMilliseconds >= IntroMilliseconds;

    public static int LogoSize(int width, int height)
    { int value = (width < height ? width : height) / 5; return value < 80 ? 80 : value > 288 ? 288 : value; }
    public static int CenterY(int height) => height * 42 / 100;
    public static int LoaderY(int width, int height) => CenterY(height) + LogoSize(width, height) / 2 + LogoSize(width, height) / 12 * 20 / 12 + 62;

    /// <summary>Caller owns the mapped extent; only the central animation region is updated.</summary>
    public static void Draw(uint* pixels, int width, int height, int stride, ulong milliseconds,
        bool waiting, int redShift = 16, int greenShift = 8, int blueShift = 0)
    {
        if (pixels == null || width < 320 || height < 240 || width > 16384 || height > 16384 || stride < width) return;
        int size = LogoSize(width, height), cx = width / 2, cy = CenterY(height);
        int top = cy - size - 12, bottom = LoaderY(width, height) + 32;
        for (int y = top < 0 ? 0 : top; y < bottom && y < height; y++)
            for (int x = cx - size - 12; x <= cx + size + 12; x++)
                if (x >= 0 && x < width) pixels[y * stride + x] = 0;
        int age = milliseconds > IntroMilliseconds ? IntroMilliseconds : (int)milliseconds;
        if (age < 160) return;
        int opacity = Ease((age - 160) * 255 / 950);
        int reveal = (age - 160) * 55 / 850;
        int drawnSize = size * (920 + 80 * opacity / 255) / 1000;
        int left = cx - drawnSize / 2, logoTop = cy - drawnSize / 2;
        fixed (char* data = BootLogoData.Pixels)
        {
            for (int y = 0; y < drawnSize; y++)
                for (int x = 0; x < drawnSize; x++)
                {
                    int sx = x * 64 / drawnSize, sy = y * 64 / drawnSize;
                    int dx = sx - 32, dy = sy - 32;
                    if (reveal < 48 && dx * dx + dy * dy > reveal * reveal) continue;
                    int at = (sy * 64 + sx) * 2;
                    uint rgb565 = data[at]; int alpha = (data[at + 1] - 32) * opacity / 255;
                    if (alpha <= 0) continue;
                    uint rgb = ((rgb565 >> 11) * 255 / 31 << 16) |
                        (((rgb565 >> 5) & 63) * 255 / 63 << 8) | ((rgb565 & 31) * 255 / 31);
                    int scan = (age - 950) * 140 / 850 - 50;
                    int band = sx + sy - scan;
                    if (age > 950 && age < 1850 && band > -5 && band < 5)
                        rgb = Blend(rgb, 0xD7F8FF, (uint)((5 - (band < 0 ? -band : band)) * 24));
                    Put(pixels, width, height, stride, left + x, logoTop + y, Dim(rgb, alpha), redShift, greenShift, blueShift);
                }
        }
        // The only typography during loading is the product's requested wordmark.
        int cell = size / 12, markHeight = cell * 20 / 12, gap = size / 70;
        if (gap < 1) gap = 1;
        int markWidth = 9 * cell + 8 * gap, markLeft = cx - markWidth / 2, markTop = cy + size / 2 + 20;
        int markOpacity = Ease((age - 700) * 255 / 900);
        fixed (char* mark = BootLogoData.Wordmark)
            for (int glyph = 0; glyph < 9; glyph++)
                for (int y = 0; y < markHeight; y++)
                    for (int x = 0; x < cell; x++)
                    {
                        int alpha = (mark[glyph * 240 + y * 20 / markHeight * 12 + x * 12 / cell] - 32) * markOpacity / 255;
                        if (alpha != 0) Put(pixels, width, height, stride, markLeft + glyph * (cell + gap) + x, markTop + y,
                            Dim(0xDBF2FF, alpha), redShift, greenShift, blueShift);
                    }
        if (age < 2000)
        {
            int fade = age < 600 ? (age - 160) * 180 / 440 : (2000 - age) * 180 / 1400;
            if (fade > 180) fade = 180;
            int phase = (int)(milliseconds % 1400) * 256 / 1400;
            Arc(pixels, width, height, stride, cx, cy, size * 3 / 4, phase, 120, Dim(Cyan, fade), redShift, greenShift, blueShift);
            Arc(pixels, width, height, stride, cx, cy, size * 3 / 4 + 5, phase + 138, 20, Dim(Cyan, fade / 3), redShift, greenShift, blueShift);
        }
        if (!waiting) return;
        int ly = LoaderY(width, height), radius = size / 10;
        if (radius < 12) radius = 12;
        int turn = (int)(milliseconds % 1200) * 256 / 1200;
        for (int tick = 0; tick < 12; tick++)
        {
            int phase = tick * 256 / 12;
            int distance = ((int)(milliseconds / 75) - tick + 12000) % 12;
            int light = distance < 5 ? 255 - distance * 43 : 25;
            int x1 = cx + Cos(phase) * (radius - 3) / 1024, y1 = ly + Sin(phase) * (radius - 3) / 1024;
            int x2 = cx + Cos(phase) * radius / 1024, y2 = ly + Sin(phase) * radius / 1024;
            Line(pixels, width, height, stride, x1, y1, x2, y2, Dim(distance == 0 ? 0xC4F4FF : Cyan, light), redShift, greenShift, blueShift);
        }
        Arc(pixels, width, height, stride, cx, ly, radius + 6, -turn, 42, Dim(Cyan, 130), redShift, greenShift, blueShift);
        Arc(pixels, width, height, stride, cx, ly, radius + 6, -turn + 128, 12, Dim(Cyan, 55), redShift, greenShift, blueShift);
        Put(pixels, width, height, stride, cx, ly, Dim(Cyan, 120 + Sin(turn) * 50 / 1024), redShift, greenShift, blueShift);
    }

    private static int Ease(int value)
    { if (value < 0) return 0; if (value > 255) return 255; return value * value * (765 - 2 * value) / 65025; }
    private static uint Dim(uint rgb, int light)
    { if (light < 0) light = 0; if (light > 255) light = 255; return ((rgb >> 16) * (uint)light / 255 << 16) | (((rgb >> 8) & 255) * (uint)light / 255 << 8) | ((rgb & 255) * (uint)light / 255); }
    private static uint Blend(uint a, uint b, uint amount) =>
        (((a >> 16) * (255 - amount) + (b >> 16) * amount) / 255 << 16) |
        ((((a >> 8) & 255) * (255 - amount) + ((b >> 8) & 255) * amount) / 255 << 8) |
        (((a & 255) * (255 - amount) + (b & 255) * amount) / 255);
    private static int Sin(int phase)
    {
        phase &= 255;
        int index = phase & 63;
        int value = BootLogoData.QuarterSine[(phase & 64) == 0 ? index : 64 - index];
        return (phase & 128) == 0 ? value : -value;
    }
    private static int Cos(int phase) => Sin(phase + 64);
    private static void Put(uint* pixels, int width, int height, int stride, int x, int y, uint rgb, int r, int g, int b)
    { if (x >= 0 && y >= 0 && x < width && y < height) pixels[y * stride + x] = ((rgb >> 16) << r) | (((rgb >> 8) & 255) << g) | ((rgb & 255) << b); }
    private static void Arc(uint* pixels, int width, int height, int stride, int cx, int cy, int radius, int start, int length, uint rgb, int r, int g, int b)
    {
        for (int phase = start; phase < start + length; phase++)
            Line(pixels, width, height, stride, cx + Cos(phase) * radius / 1024, cy + Sin(phase) * radius / 1024,
                cx + Cos(phase + 1) * radius / 1024, cy + Sin(phase + 1) * radius / 1024, rgb, r, g, b);
    }
    private static void Line(uint* pixels, int width, int height, int stride, int x, int y, int endX, int endY, uint rgb, int r, int g, int b)
    {
        int dx = endX - x; if (dx < 0) dx = -dx;
        int dy = endY - y; if (dy > 0) dy = -dy;
        int sx = x < endX ? 1 : -1, sy = y < endY ? 1 : -1, error = dx + dy;
        for (;;)
        {
            Put(pixels, width, height, stride, x, y, rgb, r, g, b);
            if (x == endX && y == endY) return;
            int twice = 2 * error;
            if (twice >= dy) { error += dy; x += sx; }
            if (twice <= dx) { error += dx; y += sy; }
        }
    }
}