[2021/02/26 Update]
These is a much easier way to handle this kind of cutting problem. Just use another layer with mask to clip the drawing. Here is the example:
HRESULT hr;
ID2D1Layer* layer = NULL;
hr = target->CreateLayer(NULL, &layer);
if (SUCCEEDED(hr))
{
target->PushLayer(
D2D1::LayerParameters(D2D1::InfiniteRect(), geomatry_mask),
layer
);
target->DrawGeometry(geometry_target, brush);
target->PopLayer();
}
if (layer != NULL)
layer->Release();
[Old]
下圖為例,如果在 scrolling area 我們有個文字並用 ID2Geometry 圍起來的區域,在往上或往下時,會需要切掉一部分。
文字部分的剪裁在 https://www.yhorng.com/blog/?p=339 這篇裡面有說明,需要用 callback 來處哩。
Geometry 的剪裁比較沒那麼直接,如果 D2D1_COMBINE_MODE_INTERSECT Combine 原始的圖片和一個外框,
上圖的虛線和實線結合後,用 Draw 會多畫了最上方的那條線,比較一開頭的圖,我們並不希望 “建構者” 三個字上面有畫一條線。
如果我們依然要使用 general solution 的 Geometry 來畫圖,而不是每次遇到不同圖形都要另外客製一條一條線自己畫 (想像一下如果非 rectangle 時有多難搞),該怎麼辦呢?
方法是與其用一個 Rectangle Geometry,我們使用二個來組成 Geometry Group。
上圖左邊實體的黑色 Rectangle 是二個 Rectangle Group,跟綠色的 Geometry 結合後,會變成右邊的圖形,由於 Direct2D 座標和大小都是浮點,所以可以畫得比預設的 1.0f 還細也沒問題。
pseudo-code
HRESULT hr;
ID2D1GeometrySink* sink = NULL;
ID2D1GeometryGroup *frame = NULL;
ID2D1PathGeometry* frame_cut = NULL;
ID2D1RectangleGeometry *rect_outer = NULL, *rect_inner = NULL, *rect_cut = NULL;
ID2D1Geometry* rect_array[2];
factory->CreateRectangleGeometry(D2D1::RectF(left, top, right, bottom), &rect_outer);
factory->CreateRectangleGeometry(D2D1::RectF(
left + stroke_width,
top + stroke_width,
right - stroke_width, bottom - stroke_width),
&rect_inner);
rect_array[0] = rect_outer;
rect_array[1] = rect_inner;
factory->CreateGeometryGroup(
D2D1_FILL_MODE_ALTERNATE,
rect_array,
ARRAYSIZE(rect_array),
&frame
);
target->FillGeometry(frame, brushes.get(target, brush);
factory->CreateRectangleGeometry(D2D1::RectF(
left,
top > low_pos + y ? top : low_pos + y,
right,
bottom < high_pos + y ? bottom : high_pos + y), &rect_cut);
factory->CreatePathGeometry(&frame_cut);
frame_cut->Open(&sink);
frame->CombineWithGeometry(rect_cut, D2D1_COMBINE_MODE_INTERSECT, NULL, sink);
sink->Close();
target->FillGeometry(frame_cut, brush);