aboutsummaryrefslogtreecommitdiff
path: root/src/libui_sdl/libui/windows/draw.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libui_sdl/libui/windows/draw.cpp')
-rw-r--r--src/libui_sdl/libui/windows/draw.cpp578
1 files changed, 0 insertions, 578 deletions
diff --git a/src/libui_sdl/libui/windows/draw.cpp b/src/libui_sdl/libui/windows/draw.cpp
deleted file mode 100644
index 9a815b9..0000000
--- a/src/libui_sdl/libui/windows/draw.cpp
+++ /dev/null
@@ -1,578 +0,0 @@
-// 7 september 2015
-#include "uipriv_windows.hpp"
-#include "draw.hpp"
-
-ID2D1Factory *d2dfactory = NULL;
-
-HRESULT initDraw(void)
-{
- D2D1_FACTORY_OPTIONS opts;
-
- ZeroMemory(&opts, sizeof (D2D1_FACTORY_OPTIONS));
- // TODO make this an option
- opts.debugLevel = D2D1_DEBUG_LEVEL_NONE;
- return D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
- IID_ID2D1Factory,
- &opts,
- (void **) (&d2dfactory));
-}
-
-void uninitDraw(void)
-{
- d2dfactory->Release();
-}
-
-ID2D1HwndRenderTarget *makeHWNDRenderTarget(HWND hwnd)
-{
- D2D1_RENDER_TARGET_PROPERTIES props;
- D2D1_HWND_RENDER_TARGET_PROPERTIES hprops;
- HDC dc;
- RECT r;
- ID2D1HwndRenderTarget *rt;
- HRESULT hr;
-
- // we need a DC for the DPI
- // we *could* just use the screen DPI but why when we have a window handle and its DC has a DPI
- dc = GetDC(hwnd);
- if (dc == NULL)
- logLastError(L"error getting DC to find DPI");
-
- ZeroMemory(&props, sizeof (D2D1_RENDER_TARGET_PROPERTIES));
- props.type = D2D1_RENDER_TARGET_TYPE_HARDWARE;//DEFAULT;
- props.pixelFormat.format = DXGI_FORMAT_UNKNOWN;
- props.pixelFormat.alphaMode = D2D1_ALPHA_MODE_UNKNOWN;
- props.dpiX = GetDeviceCaps(dc, LOGPIXELSX);
- props.dpiY = GetDeviceCaps(dc, LOGPIXELSY);
- props.usage = D2D1_RENDER_TARGET_USAGE_NONE;
- props.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
-
- if (ReleaseDC(hwnd, dc) == 0)
- logLastError(L"error releasing DC for finding DPI");
-
- uiWindowsEnsureGetClientRect(hwnd, &r);
-
- ZeroMemory(&hprops, sizeof (D2D1_HWND_RENDER_TARGET_PROPERTIES));
- hprops.hwnd = hwnd;
- hprops.pixelSize.width = r.right - r.left;
- hprops.pixelSize.height = r.bottom - r.top;
- // according to Rick Brewster, some drivers will misbehave if we don't specify this (see http://stackoverflow.com/a/33222983/3408572)
- hprops.presentOptions = D2D1_PRESENT_OPTIONS_RETAIN_CONTENTS;
-
- hr = d2dfactory->CreateHwndRenderTarget(
- &props,
- &hprops,
- &rt);
- if (hr != S_OK)
- {
- props.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
- hr = d2dfactory->CreateHwndRenderTarget(
- &props,
- &hprops,
- &rt);
- if (hr != S_OK)
- logHRESULT(L"error creating HWND render target", hr);
- }
-
- return rt;
-}
-
-ID2D1DCRenderTarget *makeHDCRenderTarget(HDC dc, RECT *r)
-{
- D2D1_RENDER_TARGET_PROPERTIES props;
- ID2D1DCRenderTarget *rt;
- HRESULT hr;
-
- ZeroMemory(&props, sizeof (D2D1_RENDER_TARGET_PROPERTIES));
- props.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
- props.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
- props.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
- props.dpiX = GetDeviceCaps(dc, LOGPIXELSX);
- props.dpiY = GetDeviceCaps(dc, LOGPIXELSY);
- props.usage = D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE;
- props.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;
-
- hr = d2dfactory->CreateDCRenderTarget(&props, &rt);
- if (hr != S_OK)
- logHRESULT(L"error creating DC render target", hr);
- hr = rt->BindDC(dc, r);
- if (hr != S_OK)
- logHRESULT(L"error binding DC to DC render target", hr);
- return rt;
-}
-
-static void resetTarget(ID2D1RenderTarget *rt)
-{
- D2D1_MATRIX_3X2_F dm;
-
- // transformations persist
- // reset to the identity matrix
- ZeroMemory(&dm, sizeof (D2D1_MATRIX_3X2_F));
- dm._11 = 1;
- dm._22 = 1;
- rt->SetTransform(&dm);
-}
-
-uiDrawContext *newContext(ID2D1RenderTarget *rt)
-{
- uiDrawContext *c;
-
- c = uiNew(uiDrawContext);
- c->rt = rt;
- c->states = new std::vector<struct drawState>;
- resetTarget(c->rt);
- return c;
-}
-
-void freeContext(uiDrawContext *c)
-{
- if (c->currentClip != NULL)
- c->currentClip->Release();
- if (c->states->size() != 0)
- // TODO do this on other platforms
- userbug("You did not balance uiDrawSave() and uiDrawRestore() calls.");
- delete c->states;
- uiFree(c);
-}
-
-static ID2D1Brush *makeSolidBrush(uiDrawBrush *b, ID2D1RenderTarget *rt, D2D1_BRUSH_PROPERTIES *props)
-{
- D2D1_COLOR_F color;
- ID2D1SolidColorBrush *brush;
- HRESULT hr;
-
- color.r = b->R;
- color.g = b->G;
- color.b = b->B;
- color.a = b->A;
-
- hr = rt->CreateSolidColorBrush(
- &color,
- props,
- &brush);
- if (hr != S_OK)
- logHRESULT(L"error creating solid brush", hr);
- return brush;
-}
-
-static ID2D1GradientStopCollection *mkstops(uiDrawBrush *b, ID2D1RenderTarget *rt)
-{
- ID2D1GradientStopCollection *s;
- D2D1_GRADIENT_STOP *stops;
- size_t i;
- HRESULT hr;
-
- stops = (D2D1_GRADIENT_STOP *) uiAlloc(b->NumStops * sizeof (D2D1_GRADIENT_STOP), "D2D1_GRADIENT_STOP[]");
- for (i = 0; i < b->NumStops; i++) {
- stops[i].position = b->Stops[i].Pos;
- stops[i].color.r = b->Stops[i].R;
- stops[i].color.g = b->Stops[i].G;
- stops[i].color.b = b->Stops[i].B;
- stops[i].color.a = b->Stops[i].A;
- }
-
- hr = rt->CreateGradientStopCollection(
- stops,
- b->NumStops,
- D2D1_GAMMA_2_2, // this is the default for the C++-only overload of ID2D1RenderTarget::GradientStopCollection()
- D2D1_EXTEND_MODE_CLAMP,
- &s);
- if (hr != S_OK)
- logHRESULT(L"error creating stop collection", hr);
-
- uiFree(stops);
- return s;
-}
-
-static ID2D1Brush *makeLinearBrush(uiDrawBrush *b, ID2D1RenderTarget *rt, D2D1_BRUSH_PROPERTIES *props)
-{
- ID2D1LinearGradientBrush *brush;
- D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES gprops;
- ID2D1GradientStopCollection *stops;
- HRESULT hr;
-
- ZeroMemory(&gprops, sizeof (D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES));
- gprops.startPoint.x = b->X0;
- gprops.startPoint.y = b->Y0;
- gprops.endPoint.x = b->X1;
- gprops.endPoint.y = b->Y1;
-
- stops = mkstops(b, rt);
-
- hr = rt->CreateLinearGradientBrush(
- &gprops,
- props,
- stops,
- &brush);
- if (hr != S_OK)
- logHRESULT(L"error creating gradient brush", hr);
-
- // the example at https://msdn.microsoft.com/en-us/library/windows/desktop/dd756682%28v=vs.85%29.aspx says this is safe to do now
- stops->Release();
- return brush;
-}
-
-static ID2D1Brush *makeRadialBrush(uiDrawBrush *b, ID2D1RenderTarget *rt, D2D1_BRUSH_PROPERTIES *props)
-{
- ID2D1RadialGradientBrush *brush;
- D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES gprops;
- ID2D1GradientStopCollection *stops;
- HRESULT hr;
-
- ZeroMemory(&gprops, sizeof (D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES));
- gprops.gradientOriginOffset.x = b->X0 - b->X1;
- gprops.gradientOriginOffset.y = b->Y0 - b->Y1;
- gprops.center.x = b->X1;
- gprops.center.y = b->Y1;
- gprops.radiusX = b->OuterRadius;
- gprops.radiusY = b->OuterRadius;
-
- stops = mkstops(b, rt);
-
- hr = rt->CreateRadialGradientBrush(
- &gprops,
- props,
- stops,
- &brush);
- if (hr != S_OK)
- logHRESULT(L"error creating gradient brush", hr);
-
- stops->Release();
- return brush;
-}
-
-static ID2D1Brush *makeBrush(uiDrawBrush *b, ID2D1RenderTarget *rt)
-{
- D2D1_BRUSH_PROPERTIES props;
-
- ZeroMemory(&props, sizeof (D2D1_BRUSH_PROPERTIES));
- props.opacity = 1.0;
- // identity matrix
- props.transform._11 = 1;
- props.transform._22 = 1;
-
- switch (b->Type) {
- case uiDrawBrushTypeSolid:
- return makeSolidBrush(b, rt, &props);
- case uiDrawBrushTypeLinearGradient:
- return makeLinearBrush(b, rt, &props);
- case uiDrawBrushTypeRadialGradient:
- return makeRadialBrush(b, rt, &props);
-// case uiDrawBrushTypeImage:
-// TODO
- }
-
- // TODO do this on all platforms
- userbug("Invalid brush type %d given to drawing operation.", b->Type);
- // TODO dummy brush?
- return NULL; // make compiler happy
-}
-
-// how clipping works:
-// every fill and stroke is done on a temporary layer with the clip geometry applied to it
-// this is really the only way to clip in Direct2D that doesn't involve opacity images
-// reference counting:
-// - initially the clip is NULL, which means do not use a layer
-// - the first time uiDrawClip() is called, we take a reference on the path passed in (this is also why uiPathEnd() is needed)
-// - every successive time, we create a new PathGeometry and merge the current clip with the new path, releasing the reference we took earlier and taking a reference to the new one
-// - in Save, we take another reference; in Restore we drop the refernece to the existing path geometry and transfer that saved ref to the new path geometry over to the context
-// uiDrawFreePath() doesn't destroy the path geometry, it just drops the reference count, so a clip can exist independent of its path
-
-static ID2D1Layer *applyClip(uiDrawContext *c)
-{
- ID2D1Layer *layer;
- D2D1_LAYER_PARAMETERS params;
- HRESULT hr;
-
- // if no clip, don't do anything
- if (c->currentClip == NULL)
- return NULL;
-
- // create a layer for clipping
- // we have to explicitly make the layer because we're still targeting Windows 7
- hr = c->rt->CreateLayer(NULL, &layer);
- if (hr != S_OK)
- logHRESULT(L"error creating clip layer", hr);
-
- // apply it as the clip
- ZeroMemory(&params, sizeof (D2D1_LAYER_PARAMETERS));
- // this is the equivalent of InfiniteRect() in d2d1helper.h
- params.contentBounds.left = -FLT_MAX;
- params.contentBounds.top = -FLT_MAX;
- params.contentBounds.right = FLT_MAX;
- params.contentBounds.bottom = FLT_MAX;
- params.geometricMask = (ID2D1Geometry *) (c->currentClip);
- // TODO is this correct?
- params.maskAntialiasMode = c->rt->GetAntialiasMode();
- // identity matrix
- params.maskTransform._11 = 1;
- params.maskTransform._22 = 1;
- params.opacity = 1.0;
- params.opacityBrush = NULL;
- params.layerOptions = D2D1_LAYER_OPTIONS_NONE;
- // TODO is this correct?
- if (c->rt->GetTextAntialiasMode() == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE)
- params.layerOptions = D2D1_LAYER_OPTIONS_INITIALIZE_FOR_CLEARTYPE;
- c->rt->PushLayer(&params, layer);
-
- // return the layer so it can be freed later
- return layer;
-}
-
-static void unapplyClip(uiDrawContext *c, ID2D1Layer *layer)
-{
- if (layer == NULL)
- return;
- c->rt->PopLayer();
- layer->Release();
-}
-
-void uiDrawStroke(uiDrawContext *c, uiDrawPath *p, uiDrawBrush *b, uiDrawStrokeParams *sp)
-{
- ID2D1Brush *brush;
- ID2D1StrokeStyle *style;
- D2D1_STROKE_STYLE_PROPERTIES dsp;
- FLOAT *dashes;
- size_t i;
- ID2D1Layer *cliplayer;
- HRESULT hr;
-
- brush = makeBrush(b, c->rt);
-
- ZeroMemory(&dsp, sizeof (D2D1_STROKE_STYLE_PROPERTIES));
- switch (sp->Cap) {
- case uiDrawLineCapFlat:
- dsp.startCap = D2D1_CAP_STYLE_FLAT;
- dsp.endCap = D2D1_CAP_STYLE_FLAT;
- dsp.dashCap = D2D1_CAP_STYLE_FLAT;
- break;
- case uiDrawLineCapRound:
- dsp.startCap = D2D1_CAP_STYLE_ROUND;
- dsp.endCap = D2D1_CAP_STYLE_ROUND;
- dsp.dashCap = D2D1_CAP_STYLE_ROUND;
- break;
- case uiDrawLineCapSquare:
- dsp.startCap = D2D1_CAP_STYLE_SQUARE;
- dsp.endCap = D2D1_CAP_STYLE_SQUARE;
- dsp.dashCap = D2D1_CAP_STYLE_SQUARE;
- break;
- }
- switch (sp->Join) {
- case uiDrawLineJoinMiter:
- dsp.lineJoin = D2D1_LINE_JOIN_MITER_OR_BEVEL;
- dsp.miterLimit = sp->MiterLimit;
- break;
- case uiDrawLineJoinRound:
- dsp.lineJoin = D2D1_LINE_JOIN_ROUND;
- break;
- case uiDrawLineJoinBevel:
- dsp.lineJoin = D2D1_LINE_JOIN_BEVEL;
- break;
- }
- dsp.dashStyle = D2D1_DASH_STYLE_SOLID;
- dashes = NULL;
- // note that dash widths and the dash phase are scaled up by the thickness by Direct2D
- // TODO be sure to formally document this
- if (sp->NumDashes != 0) {
- dsp.dashStyle = D2D1_DASH_STYLE_CUSTOM;
- dashes = (FLOAT *) uiAlloc(sp->NumDashes * sizeof (FLOAT), "FLOAT[]");
- for (i = 0; i < sp->NumDashes; i++)
- dashes[i] = sp->Dashes[i] / sp->Thickness;
- }
- dsp.dashOffset = sp->DashPhase / sp->Thickness;
- hr = d2dfactory->CreateStrokeStyle(
- &dsp,
- dashes,
- sp->NumDashes,
- &style);
- if (hr != S_OK)
- logHRESULT(L"error creating stroke style", hr);
- if (sp->NumDashes != 0)
- uiFree(dashes);
-
- cliplayer = applyClip(c);
- c->rt->DrawGeometry(
- pathGeometry(p),
- brush,
- sp->Thickness,
- style);
- unapplyClip(c, cliplayer);
-
- style->Release();
- brush->Release();
-}
-
-void uiDrawFill(uiDrawContext *c, uiDrawPath *p, uiDrawBrush *b)
-{
- ID2D1Brush *brush;
- ID2D1Layer *cliplayer;
-
- brush = makeBrush(b, c->rt);
- cliplayer = applyClip(c);
- c->rt->FillGeometry(
- pathGeometry(p),
- brush,
- NULL);
- unapplyClip(c, cliplayer);
- brush->Release();
-}
-
-void uiDrawTransform(uiDrawContext *c, uiDrawMatrix *m)
-{
- D2D1_MATRIX_3X2_F dm, cur;
-
- c->rt->GetTransform(&cur);
- m2d(m, &dm);
- // you would think we have to do already * m, right?
- // WRONG! we have to do m * already
- // why? a few reasons
- // a) this lovely comment in cairo's source - http://cgit.freedesktop.org/cairo/tree/src/cairo-matrix.c?id=0537479bd1d4c5a3bc0f6f41dec4deb98481f34a#n330
- // Direct2D uses column vectors and I don't know if this is even documented
- // b) that's what Core Graphics does
- // TODO see if Microsoft says to do this
- dm = dm * cur; // for whatever reason operator * is defined but not operator *=
- c->rt->SetTransform(&dm);
-}
-
-void uiDrawClip(uiDrawContext *c, uiDrawPath *path)
-{
- ID2D1PathGeometry *newPath;
- ID2D1GeometrySink *newSink;
- HRESULT hr;
-
- // if there's no current clip, borrow the path
- if (c->currentClip == NULL) {
- c->currentClip = pathGeometry(path);
- // we have to take our own reference to that clip
- c->currentClip->AddRef();
- return;
- }
-
- // otherwise we have to intersect the current path with the new one
- // we do that into a new path, and then replace c->currentClip with that new path
- hr = d2dfactory->CreatePathGeometry(&newPath);
- if (hr != S_OK)
- logHRESULT(L"error creating new path", hr);
- hr = newPath->Open(&newSink);
- if (hr != S_OK)
- logHRESULT(L"error opening new path", hr);
- hr = c->currentClip->CombineWithGeometry(
- pathGeometry(path),
- D2D1_COMBINE_MODE_INTERSECT,
- NULL,
- // TODO is this correct or can this be set per target?
- D2D1_DEFAULT_FLATTENING_TOLERANCE,
- newSink);
- if (hr != S_OK)
- logHRESULT(L"error intersecting old path with new path", hr);
- hr = newSink->Close();
- if (hr != S_OK)
- logHRESULT(L"error closing new path", hr);
- newSink->Release();
-
- // okay we have the new clip; we just need to replace the old one with it
- c->currentClip->Release();
- c->currentClip = newPath;
- // we have a reference already; no need for another
-}
-
-struct drawState {
- ID2D1DrawingStateBlock *dsb;
- ID2D1PathGeometry *clip;
-};
-
-void uiDrawSave(uiDrawContext *c)
-{
- struct drawState state;
- HRESULT hr;
-
- hr = d2dfactory->CreateDrawingStateBlock(
- // TODO verify that these are correct
- NULL,
- NULL,
- &(state.dsb));
- if (hr != S_OK)
- logHRESULT(L"error creating drawing state block", hr);
- c->rt->SaveDrawingState(state.dsb);
-
- // if we have a clip, we need to hold another reference to it
- if (c->currentClip != NULL)
- c->currentClip->AddRef();
- state.clip = c->currentClip; // even if NULL assign it
-
- c->states->push_back(state);
-}
-
-void uiDrawRestore(uiDrawContext *c)
-{
- struct drawState state;
-
- state = (*(c->states))[c->states->size() - 1];
- c->states->pop_back();
-
- c->rt->RestoreDrawingState(state.dsb);
- state.dsb->Release();
-
- // if we have a current clip, we need to drop it
- if (c->currentClip != NULL)
- c->currentClip->Release();
- // no need to explicitly addref or release; just transfer the ref
- c->currentClip = state.clip;
-}
-
-
-// bitmap API
-
-uiDrawBitmap* uiDrawNewBitmap(uiDrawContext* c, int width, int height, int alpha)
-{
- uiDrawBitmap* bmp;
- HRESULT hr;
-
- bmp = uiNew(uiDrawBitmap);
-
- D2D1_BITMAP_PROPERTIES bp2 = D2D1::BitmapProperties();
- bp2.dpiX = 0;
- bp2.dpiY = 0;
- bp2.pixelFormat = D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,
- alpha ? D2D1_ALPHA_MODE_PREMULTIPLIED : D2D1_ALPHA_MODE_IGNORE);
-
- //c->rt->BeginDraw();
-
- hr = c->rt->CreateBitmap(D2D1::SizeU(width,height), NULL, 0, &bp2, &bmp->bmp);
- if (hr != S_OK)
- logHRESULT(L"error creating bitmap", hr);
-
- //c->rt->EndDraw();
-
- bmp->Width = width;
- bmp->Height = height;
- bmp->Stride = width*4;
-
- return bmp;
-}
-
-void uiDrawBitmapUpdate(uiDrawBitmap* bmp, const void* data)
-{
- D2D1_RECT_U rekt = D2D1::RectU(0, 0, bmp->Width, bmp->Height);
- bmp->bmp->CopyFromMemory(&rekt, data, bmp->Stride);
-}
-
-void uiDrawBitmapDraw(uiDrawContext* c, uiDrawBitmap* bmp, uiRect* srcrect, uiRect* dstrect, int filter)
-{
- D2D_RECT_F _srcrect = D2D1::RectF(srcrect->X, srcrect->Y, srcrect->X+srcrect->Width, srcrect->Y+srcrect->Height);
- D2D_RECT_F _dstrect = D2D1::RectF(dstrect->X, dstrect->Y, dstrect->X+dstrect->Width, dstrect->Y+dstrect->Height);
-
- float dpix, dpiy;
- c->rt->GetDpi(&dpix, &dpiy);
- _srcrect.left = (_srcrect.left * 96.0f) / dpix;
- _srcrect.top = (_srcrect.top * 96.0f) / dpiy;
- _srcrect.right = (_srcrect.right * 96.0f) / dpix;
- _srcrect.bottom = (_srcrect.bottom * 96.0f) / dpiy;
-
- c->rt->DrawBitmap(bmp->bmp, &_dstrect, 1.0f, filter ? D2D1_BITMAP_INTERPOLATION_MODE_LINEAR : D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, &_srcrect);
-}
-
-void uiDrawFreeBitmap(uiDrawBitmap* bmp)
-{
- bmp->bmp->Release();
- uiFree(bmp);
-}