diff --git a/matrix.c b/matrix.c index d98cdc92..6af1bcee 100644 --- a/matrix.c +++ b/matrix.c @@ -15,3 +15,67 @@ void setIdentity(uiDrawMatrix *m) // TODO skew // see windows/draw.c for more information +// TODO we don't need to do this if we can bypass the multiplication somehow + +void fallbackTranslate(uiDrawMatrix *m, double x, double y) +{ + uiDrawMatrix m2; + + setIdentity(&m2); + m2.M31 = x; + m2.M32 = y; + fallbackMultiply(m, &m2); +} + +// TODO the Direct2D version takes a center argument; investigate it +void fallbackScale(uiDrawMatrix *m, double x, double y) +{ + uiDrawMatrix m2; + + setIdentity(&m2); + m2.M11 = x; + m2.M22 = y; + fallbackMultiply(m, &m2); +} + +void fallbackMultiply(uiDrawMatrix *dest, uiDrawMatrix *src) +{ + uiDrawMatrix out; + + out.M11 = (dest->M11 * src->M11) + + (dest->M12 * src->M21); + out.M12 = (dest->M11 * src->M12) + + (dest->M12 * src->M22); + out.M21 = (dest->M21 * src->M11) + + (dest->M22 * src->M21); + out.M22 = (dest->M21 * src->M12) + + (dest->M22 * src->M22); + out.M31 = (dest->M31 * src->M11) + + (dest->M32 * src->M21) + + src->M31; + out.M32 = (dest->M31 * src->M12) + + (dest->M32 * src->M22) + + src->M32; + *dest = out; +} + +void fallbackTransformPoint(uiDrawMatrix *m, double *x, double *y) +{ + double xout, yout; + + xout = (*x * m->M11) + (*y * m->M21) + m->M31; + yout = (*x * m->M12) + (*y * m->M22) + m->M32; + *x = xout; + *y = yout; +} + +// and this algorithm is according to cairo +void fallbackTransformSize(uiDrawMatrix *m, double *x, double *y) +{ + double xout, yout; + + xout = (*x * m->M11) + (*y * m->M21); + yout = (*x * m->M12) + (*y * m->M22); + *x = xout; + *y = yout; +} diff --git a/ui.h b/ui.h index c8995dea..95f310c1 100644 --- a/ui.h +++ b/ui.h @@ -416,7 +416,7 @@ _UI_EXTERN void uiDrawMatrixSetIdentity(uiDrawMatrix *m); _UI_EXTERN void uiDrawMatrixTranslate(uiDrawMatrix *m, double x, double y); _UI_EXTERN void uiDrawMatrixScale(uiDrawMatrix *m, double x, double y); _UI_EXTERN void uiDrawMatrixRotate(uiDrawMatrix *m, double x, double y, double amount); -_UI_EXTERN void uiDrawMatrixSkew(uiDrawMatrix *m, double x, double y, double amount); +_UI_EXTERN void uiDrawMatrixSkew(uiDrawMatrix *m, double x, double y, double xamount, double yamount); _UI_EXTERN void uiDrawMatrixMultiply(uiDrawMatrix *dest, uiDrawMatrix *src); _UI_EXTERN int uiDrawMatrixInvertible(uiDrawMatrix *m); _UI_EXTERN int uiDrawMatrixInvert(uiDrawMatrix *m); diff --git a/uipriv.h b/uipriv.h index 641e2aac..d2410cdc 100644 --- a/uipriv.h +++ b/uipriv.h @@ -62,3 +62,8 @@ extern int fromScancode(uintptr_t, uiAreaKeyEvent *); // matrix.c extern void setIdentity(uiDrawMatrix *); +extern void fallbackTranslate(uiDrawMatrix *, double, double); +extern void fallbackScale(uiDrawMatrix *, double, double); +extern void fallbackMultiply(uiDrawMatrix *, uiDrawMatrix *); +extern void fallbackTransformPoint(uiDrawMatrix *, double *, double *); +extern void fallbackTransformSize(uiDrawMatrix *, double *, double *); diff --git a/unix/area.c b/unix/area.c index 917ed6df..2d4bc8d4 100644 --- a/unix/area.c +++ b/unix/area.c @@ -178,7 +178,7 @@ static gboolean areaWidget_draw(GtkWidget *w, cairo_t *cr) dp.HScrollPos = gtk_adjustment_get_value(ap->ha); dp.VScrollPos = gtk_adjustment_get_value(ap->va); - // no need to save or restore the graphics state; GTK+ does that for us + // no need to save or restore the graphics state to reset transformations; GTK+ does that for us (*(ap->ah->Draw))(ap->ah, ap->a, &dp); freeContext(dp.Context); diff --git a/unix/draw.c b/unix/draw.c index 3d59aad2..2bcbce78 100644 --- a/unix/draw.c +++ b/unix/draw.c @@ -353,7 +353,7 @@ void uiDrawMatrixRotate(uiDrawMatrix *m, double x, double y, double amount) c2m(&c, m); } -void uiDrawMatrixSkew(uiDrawMatrix *m, double x, double y, double amount) +void uiDrawMatrixSkew(uiDrawMatrix *m, double x, double y, double amount, double yamount) { complain("TODO"); } diff --git a/windows/area.c b/windows/area.c index 389e89ef..14fe477e 100644 --- a/windows/area.c +++ b/windows/area.c @@ -54,6 +54,7 @@ static HRESULT doPaint(uiArea *a, ID2D1RenderTarget *rt, RECT *client, RECT *cli bgcolor.a = 1.0; ID2D1RenderTarget_Clear(rt, &bgcolor); + // no need to save or restore the graphics state to reset transformations; it's handled by resetTarget() in draw.c, called by newContext() above (*(ah->Draw))(ah, a, &dp); freeContext(dp.Context); diff --git a/windows/draw.c b/windows/draw.c index 5235d558..9fab786d 100644 --- a/windows/draw.c +++ b/windows/draw.c @@ -294,12 +294,25 @@ struct uiDrawContext { ID2D1RenderTarget *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; + ID2D1RenderTarget_SetTransform(rt, &dm); +} + uiDrawContext *newContext(ID2D1RenderTarget *rt) { uiDrawContext *c; c = uiNew(uiDrawContext); c->rt = rt; + resetTarget(c->rt); return c; } @@ -512,3 +525,128 @@ void uiDrawFill(uiDrawContext *c, uiDrawPath *p, uiDrawBrush *b) NULL); ID2D1Brush_Release(brush); } + +static void m2d(uiDrawMatrix *m, D2D1_MATRIX_3X2_F *d) +{ + d->_11 = m->M11; + d->_12 = m->M12; + d->_21 = m->M21; + d->_22 = m->M22; + d->_31 = m->M31; + d->_32 = m->M32; +} + +static void d2m(D2D1_MATRIX_3X2_F *d, uiDrawMatrix *m) +{ + m->M11 = d->_11; + m->M12 = d->_12; + m->M21 = d->_21; + m->M22 = d->_22; + m->M31 = d->_31; + m->M32 = d->_32; +} + +void uiDrawMatrixSetIdentity(uiDrawMatrix *m) +{ + setIdentity(m); +} + +// frustratingly all of the operations on a matrix except rotation and skeweing are provided by the C++-only d2d1helper.h +// we'll have to recreate their functionalities here +// the implementations are all in the main matrix.c file + +void uiDrawMatrixTranslate(uiDrawMatrix *m, double x, double y) +{ + fallbackTranslate(m, x, y); +} + +void uiDrawMatrixScale(uiDrawMatrix *m, double x, double y) +{ + fallbackScale(m, x, y); +} + +void uiDrawMatrixRotate(uiDrawMatrix *m, double x, double y, double amount) +{ + D2D1_POINT_2F center; + D2D1_MATRIX_3X2_F dm; + uiDrawMatrix rm; + + amount *= 180 / M_PI; // must be in degrees + center.x = x; + center.y = y; + // TODO explain this + D2D1MakeRotateMatrix(-amount, center, &dm); + d2m(&dm, &rm); + uiDrawMatrixMultiply(m, &rm); +} + +void uiDrawMatrixSkew(uiDrawMatrix *m, double x, double y, double xamount, double yamount) +{ + D2D1_POINT_2F center; + D2D1_MATRIX_3X2_F dm; + uiDrawMatrix sm; + + xamount *= 180 / M_PI; // must be in degrees + yamount *= 180 / M_PI; // must be in degrees + center.x = x; + center.y = y; + D2D1MakeSkewMatrix(xamount, yamount, center, &dm); + d2m(&dm, &sm); + uiDrawMatrixMultiply(m, &sm); +} + +void uiDrawMatrixMultiply(uiDrawMatrix *dest, uiDrawMatrix *src) +{ + fallbackMultiply(dest, src); +} + +int uiDrawMatrixInvertible(uiDrawMatrix *m) +{ + D2D1_MATRIX_3X2_F d; + + m2d(m, &d); + return D2D1IsMatrixInvertible(&d) != FALSE; +} + +int uiDrawMatrixInvert(uiDrawMatrix *m) +{ + D2D1_MATRIX_3X2_F d; + + m2d(m, &d); + if (D2D1InvertMatrix(&d) == FALSE) + return 0; + d2m(&d, m); + return 1; +} + +void uiDrawMatrixTransformPoint(uiDrawMatrix *m, double *x, double *y) +{ + fallbackTransformPoint(m, x, y); +} + +void uiDrawMatrixTransformSize(uiDrawMatrix *m, double *x, double *y) +{ + fallbackTransformSize(m, x, y); +} + +void uiDrawSave(uiDrawContext *c) +{ + // TODO +} + +void uiDrawRestore(uiDrawContext *c) +{ + // TODO +} + +void uiDrawTransform(uiDrawContext *c, uiDrawMatrix *m) +{ + D2D1_MATRIX_3X2_F dm; + uiDrawMatrix already; + + ID2D1RenderTarget_GetTransform(c->rt, &dm); + d2m(&dm, &already); + uiDrawMatrixMultiply(&already, m); + m2d(&already, &dm); + ID2D1RenderTarget_SetTransform(c->rt, &dm); +}