firefox/chrome如何绘制滚动条?

Jic*_*hao 5 user-interface winapi scrollbar

chrome或firefox中的Scrollbas没有句柄.它们是无手控制,但它们与系统默认滚动条共享相同的外观和行为.从中我们可以推断出这些浏览器使用Windows主题API(如DrawThemeBackground)来绘制滚动条.

然而,诸如DrawThemeBackground之类的API是GDI,chrome/firefox使用skia/cario来渲染整个画布.我的问题是他们如何结合这两种技术?

Pesudo代码:

WM_PAINT:
    prepare canvas;
    draw the canvas with skia/cario;
    bitblt to the dc;
    draw the theme-related handless control;(???)
    bitblt to the dc or directly draw to the dc;(???)
Run Code Online (Sandbox Code Playgroud)

程序是否与上述程序相似?

Jic*_*hao 5

火狐

实际上,cairo具有从cairo表面获得DC的功能.示例代码:

VOID OnPaint(HWND hwnd, HDC hdc)
{
    RECT rc;
    ::GetClientRect(hwnd, &rc);
    //draw one line
    cairo_surface_t* surface = cairo_win32_surface_create(hdc);
    cairo_t* cr = cairo_create(surface);
    cairo_set_source_rgb(cr, 0xff, 0, 0);
    cairo_set_line_width(cr, 1);
    cairo_move_to(cr, 0, 0);
    cairo_line_to(cr, rc.right, rc.bottom);
    cairo_stroke(cr);
    cairo_destroy(cr);

    //draw the theme background
    HDC hdcNew = cairo_win32_surface_get_dc(surface);
    HTHEME hTheme = OpenThemeData(NULL, L"SCROLLBAR");
    RECT rcArrow;
    SetRect(&rcArrow, 30, 30, 45, 45);
    DrawThemeBackground(hTheme, hdcNew, SBP_ARROWBTN, ABS_DOWNDISABLED, &rcArrow, NULL);
    cairo_surface_destroy(surface);
}
Run Code Online (Sandbox Code Playgroud)

gfxWindowsNativeDrawing::BeginNativeDrawing()调用HDC gfxWindowsSurface::GetDCWithClip(gfxContext *ctx)最后调用Win32的开罗.

Skia不提供从skia canvas到hdc的转换功能,但项目chrome为skia添加了扩展并实现了此功能.

在skia/ext/bitmap_platform_device_win.cc中:

HDC BitmapPlatformDevice::BitmapPlatformDeviceData::GetBitmapDC() {
}
Run Code Online (Sandbox Code Playgroud)

从canvas内部位图创建内存dc.

所以当油漆时,无论你是否需要使用原生DC或普通的cairo/skia帆布都不再重要.

示例代码:

void TestChromeExt(HWND hwnd, HDC hdc)
{
    RECT rc;
    GetClientRect(hwnd, &rc);
    skia::BitmapPlatformDevice* pBmpDevice = skia::BitmapPlatformDevice::Create(rc.right, rc.bottom, true);
    skia::PlatformCanvas *pCanvas = new skia::PlatformCanvas(pBmpDevice);
    pCanvas->clear(SK_ColorWHITE);
    SkPaint paint;
    paint.setColor(SK_ColorRED);
    paint.setStrokeWidth(3);
    paint.setStyle(SkPaint::kStroke_Style);
    pCanvas->drawLine(0, 0, rc.right, rc.bottom, paint);
    HDC memdc = skia::BeginPlatformPaint(pCanvas);
    RECT rcArrow;
    SetRect(&rcArrow, 100, 200, 120, 220);
    DrawThemeBackground(OpenThemeData(NULL, L"SCROLLBAR"), memdc, SBP_ARROWBTN, ABS_DOWNDISABLED, &rcArrow, NULL);
    skia::EndPlatformPaint(pCanvas);
    skia::DrawToNativeContext(pCanvas, hdc, 0, 0, &rc);
}
Run Code Online (Sandbox Code Playgroud)

对于Windows,为了确保原生外观,当启用视觉样式时,这些浏览器将使用DrawThemeBackground.但是,当禁用视觉样式(经典模式为1)时,它们将用于DrawFrameControl绘制控件.