gradient stuff and windows tweaks

This commit is contained in:
Adam D. Ruppe 2021-11-23 17:23:42 -05:00
parent b698de70fc
commit 9e9be7cc0d
1 changed files with 285 additions and 33 deletions

View File

@ -3786,6 +3786,7 @@ struct EventLoopImpl {
}
} else if(waitResult == handles.length + WAIT_OBJECT_0) {
// message ready
int count;
while(PeekMessage(&message, null, 0, 0, PM_NOREMOVE)) { // need to peek since sometimes MsgWaitForMultipleObjectsEx returns even though GetMessage can block. tbh i don't fully understand it but the docs say it is foreground activation
ret = GetMessage(&message, null, 0, 0);
if(ret == -1)
@ -3793,6 +3794,10 @@ struct EventLoopImpl {
TranslateMessage(&message);
DispatchMessage(&message);
count++;
if(count > 10)
break; // take the opportunity to catch up on other events
if(ret == 0) // WM_QUIT
break;
}
@ -8535,7 +8540,7 @@ struct ScreenPainter {
impl.drawArc(upperLeft.x, upperLeft.y, width, height, start, finish);
}
//this function draws a circle with the drawEllipse() function above, it requires the upper left point and the radius
/// this function draws a circle with the drawEllipse() function above, it requires the upper left point and the radius
void drawCircle(Point upperLeft, int diameter) {
drawEllipse(upperLeft, Point(upperLeft.x + diameter, upperLeft.y + diameter));
}
@ -8657,6 +8662,26 @@ class Sprite : CapableOfBeingDrawnUpon {
private Picture xrenderPicture;
}
version(X11)
private static void requireXRender() {
if(!XRenderLibrary.loadAttempted) {
XRenderLibrary.loadDynamicLibrary();
}
if(!XRenderLibrary.loadSuccessful)
throw new Exception("XRender library load failure");
auto display = XDisplayConnection.get;
// FIXME: if we migrate X displays, these need to be changed
if(RGB24 is null)
RGB24 = XRenderFindStandardFormat(display, PictStandardRGB24);
if(ARGB32 is null)
ARGB32 = XRenderFindStandardFormat(display, PictStandardARGB32);
}
protected this() {}
this(SimpleWindow win, int width, int height, bool enableAlpha = false) {
this._width = width;
this._height = height;
@ -8666,22 +8691,12 @@ class Sprite : CapableOfBeingDrawnUpon {
auto display = XDisplayConnection.get();
if(enableAlpha) {
if(!XRenderLibrary.loadAttempted) {
XRenderLibrary.loadDynamicLibrary();
}
if(!XRenderLibrary.loadSuccessful)
throw new Exception("XRender library load failure");
requireXRender();
}
handle = XCreatePixmap(display, cast(Drawable) win.window, width, height, enableAlpha ? 32 : DefaultDepthOfDisplay(display));
if(enableAlpha) {
if(RGB24 is null)
RGB24 = XRenderFindStandardFormat(display, PictStandardRGB24);
if(ARGB32 is null)
ARGB32 = XRenderFindStandardFormat(display, PictStandardARGB32);
XRenderPictureAttributes attrs;
xrenderPicture = XRenderCreatePicture(display, handle, ARGB32, 0, &attrs);
}
@ -8816,6 +8831,213 @@ class Sprite : CapableOfBeingDrawnUpon {
}
/++
Represents a display-side gradient pseudo-image. Actually construct it with [LinearGradient], [RadialGradient], or [ConicalGradient].
History:
Added November 20, 2021 (dub v10.4)
+/
abstract class Gradient : Sprite {
protected this(int w, int h) {
version(X11) {
Sprite.requireXRender();
super();
enableAlpha = true;
_width = w;
_height = h;
} else version(Windows) {
super(null, w, h, true); // on Windows i'm just making a bitmap myself
}
}
version(Windows)
final void forEachPixel(scope Color delegate(int x, int y) dg) {
auto ptr = rawData;
foreach(j; 0 .. _height)
foreach(i; 0 .. _width) {
auto color = dg(i, _height - j - 1); // cuz of upside down bitmap
*rawData = (color.a * color.b) / 255; rawData++;
*rawData = (color.a * color.g) / 255; rawData++;
*rawData = (color.a * color.r) / 255; rawData++;
*rawData = color.a; rawData++;
}
}
version(X11)
protected void helper(scope Stop[] stops, scope Picture delegate(scope XFixed[] stopsPositions, scope XRenderColor[] colors) dg) {
assert(stops.length > 0);
assert(stops.length <= 16, "I got lazy with buffers");
XFixed[16] stopsPositions = void;
XRenderColor[16] colors = void;
foreach(idx, stop; stops) {
stopsPositions[idx] = cast(int)(stop.percentage * ushort.max);
auto c = stop.c;
colors[idx] = XRenderColor(
cast(ushort)(c.r * ushort.max / 255),
cast(ushort)(c.g * ushort.max / 255),
cast(ushort)(c.b * ushort.max / 255),
cast(ushort)(c.a * ubyte.max) // max value here is fractional
);
}
xrenderPicture = dg(stopsPositions, colors);
}
///
static struct Stop {
float percentage; /// between 0 and 1.0
Color c;
}
}
/++
Creates a linear gradient between p1 and p2.
X ONLY RIGHT NOW
History:
Added November 20, 2021 (dub v10.4)
Bugs:
Not yet implemented on Windows.
+/
class LinearGradient : Gradient {
/++
+/
this(Point p1, Point p2, Stop[] stops...) {
super(p2.x, p2.y);
version(X11) {
XLinearGradient gradient;
gradient.p1 = XPointFixed(p1.x * ushort.max, p1.y * ushort.max);
gradient.p2 = XPointFixed(p2.x * ushort.max, p2.y * ushort.max);
helper(stops, (stopsPositions, colors) {
return XRenderCreateLinearGradient(
XDisplayConnection.get,
&gradient,
stopsPositions.ptr,
colors.ptr,
cast(int) stops.length);
});
} else version(Windows) {
// FIXME
forEachPixel((int x, int y) {
import core.stdc.math;
//sqrtf(
return Color.transparent;
// return Color(x * 2, y * 2, 0, 128); // this result is so beautiful
});
}
}
}
/++
A conical gradient goes from color to color around a circumference from a center point.
X ONLY RIGHT NOW
History:
Added November 20, 2021 (dub v10.4)
Bugs:
Not yet implemented on Windows.
+/
class ConicalGradient : Gradient {
/++
+/
this(Point center, float angleInDegrees, Stop[] stops...) {
super(center.x * 2, center.y * 2);
version(X11) {
XConicalGradient gradient;
gradient.center = XPointFixed(center.x * ushort.max, center.y * ushort.max);
gradient.angle = cast(int)(angleInDegrees * ushort.max);
helper(stops, (stopsPositions, colors) {
return XRenderCreateConicalGradient(
XDisplayConnection.get,
&gradient,
stopsPositions.ptr,
colors.ptr,
cast(int) stops.length);
});
} else version(Windows) {
// FIXME
forEachPixel((int x, int y) {
import core.stdc.math;
//sqrtf(
return Color.transparent;
// return Color(x * 2, y * 2, 0, 128); // this result is so beautiful
});
}
}
}
/++
A radial gradient goes from color to color based on distance from the center.
It is like rings of color.
X ONLY RIGHT NOW
More specifically, you create two circles: an inner circle and an outer circle.
The gradient is only drawn in the area outside the inner circle but inside the outer
circle. The closest line between those two circles forms the line for the gradient
and the stops are calculated the same as the [LinearGradient]. Then it just sweeps around.
History:
Added November 20, 2021 (dub v10.4)
Bugs:
Not yet implemented on Windows.
+/
class RadialGradient : Gradient {
/++
+/
this(Point innerCenter, float innerRadius, Point outerCenter, float outerRadius, Stop[] stops...) {
super(cast(int)(outerCenter.x + outerRadius + 0.5), cast(int)(outerCenter.y + outerRadius + 0.5));
version(X11) {
XRadialGradient gradient;
gradient.inner = XCircle(innerCenter.x * ushort.max, innerCenter.y * ushort.max, cast(int) (innerRadius * ushort.max));
gradient.outer = XCircle(outerCenter.x * ushort.max, outerCenter.y * ushort.max, cast(int) (outerRadius * ushort.max));
helper(stops, (stopsPositions, colors) {
return XRenderCreateRadialGradient(
XDisplayConnection.get,
&gradient,
stopsPositions.ptr,
colors.ptr,
cast(int) stops.length);
});
} else version(Windows) {
// FIXME
forEachPixel((int x, int y) {
import core.stdc.math;
//sqrtf(
return Color.transparent;
// return Color(x * 2, y * 2, 0, 128); // this result is so beautiful
});
}
}
}
/+
NOT IMPLEMENTED
A display-stored image optimized for relatively quick drawing, like
@ -10564,6 +10786,9 @@ version(Windows) {
SelectObject(hdcBmp, oldPen);
DeleteDC(hdcBmp);
bmpWidth = width;
bmpHeight = height;
ReleaseDC(hwnd, hdc); // we keep this in opengl mode since it is a class member now
}
@ -10793,6 +11018,9 @@ version(Windows) {
int oldHeight;
bool inSizeMove;
int bmpWidth;
int bmpHeight;
// the extern(Windows) wndproc should just forward to this
LRESULT windowProcedure(HWND hwnd, uint msg, WPARAM wParam, LPARAM lParam) {
assert(hwnd is this.hwnd);
@ -10841,6 +11069,11 @@ version(Windows) {
if(!inSizeMove)
goto size_changed;
break;
/+
case WM_SIZING:
import std.stdio; writeln("size");
break;
+/
// I don't like the tearing I get when redrawing on WM_SIZE
// (I know there's other ways to fix that but I don't like that behavior anyway)
// so instead it is going to redraw only at the end of a size.
@ -10851,39 +11084,44 @@ version(Windows) {
break;
case 0x0232: /* WM_EXITSIZEMOVE */
inSizeMove = false;
size_changed:
// nothing relevant changed, don't bother redrawing
if(oldWidth == width && oldHeight == height)
break;
size_changed:
// note: OpenGL windows don't use a backing bmp, so no need to change them
// if resizability is anything other than allowResizing, it is meant to either stretch the one image or just do nothing
if(openglMode == OpenGlOptions.no) { // && resizability == Resizability.allowResizing) {
// gotta get the double buffer bmp to match the window
// FIXME: could this be more efficient? It isn't really necessary to make
// a new buffer if we're sizing down at least.
auto hdc = GetDC(hwnd);
auto oldBuffer = buffer;
buffer = CreateCompatibleBitmap(hdc, width, height);
// FIXME: could this be more efficient? it never relinquishes a large bitmap
if(width > bmpWidth || height > bmpHeight) {
auto hdc = GetDC(hwnd);
auto oldBuffer = buffer;
buffer = CreateCompatibleBitmap(hdc, width, height);
auto hdcBmp = CreateCompatibleDC(hdc);
auto oldBmp = SelectObject(hdcBmp, buffer);
auto hdcBmp = CreateCompatibleDC(hdc);
auto oldBmp = SelectObject(hdcBmp, buffer);
auto hdcOldBmp = CreateCompatibleDC(hdc);
auto oldOldBmp = SelectObject(hdcOldBmp, oldBmp);
auto hdcOldBmp = CreateCompatibleDC(hdc);
auto oldOldBmp = SelectObject(hdcOldBmp, oldBmp);
BitBlt(hdcBmp, 0, 0, width, height, hdcOldBmp, oldWidth, oldHeight, SRCCOPY);
BitBlt(hdcBmp, 0, 0, width, height, hdcOldBmp, oldWidth, oldHeight, SRCCOPY);
SelectObject(hdcOldBmp, oldOldBmp);
DeleteDC(hdcOldBmp);
bmpWidth = width;
bmpHeight = height;
SelectObject(hdcBmp, oldBmp);
DeleteDC(hdcBmp);
SelectObject(hdcOldBmp, oldOldBmp);
DeleteDC(hdcOldBmp);
ReleaseDC(hwnd, hdc);
SelectObject(hdcBmp, oldBmp);
DeleteDC(hdcBmp);
DeleteObject(oldBuffer);
ReleaseDC(hwnd, hdc);
DeleteObject(oldBuffer);
}
}
version(without_opengl) {} else
@ -11067,7 +11305,7 @@ version(Windows) {
rawData[offset + 1] = (a * what[idx + 1]) / 255; // g
rawData[offset + 0] = (a * what[idx + 2]) / 255; // b
rawData[offset + 3] = a; // a
premultiplyBgra(rawData[offset .. offset + 4]);
//premultiplyBgra(rawData[offset .. offset + 4]);
offset++;
} else {
rawData[offset + 2] = what[idx + 0]; // r
@ -16897,6 +17135,8 @@ extern(System) nothrow @nogc {
GLint glGetUniformLocation(GLuint, const(GLchar)*);
void glGenBuffers(GLsizei, GLuint*);
void glUniform4fv(GLint, GLsizei, const(GLfloat)*);
void glUniform1f(GLint, float);
void glUniform2f(GLint, float, float);
void glUniform4f(GLint, float, float, float, float);
void glColorMask(GLboolean, GLboolean, GLboolean, GLboolean);
void glStencilOpSeparate(GLenum, GLenum, GLenum, GLenum);
@ -17323,8 +17563,19 @@ final class OpenGlShader {
/// Assigns the 4 floats. You will probably have to call this via the .opAssign name
void opAssign(float x, float y, float z, float w) {
if(id != -1)
glUniform4f(id, x, y, z, w);
}
void opAssign(float x) {
if(id != -1)
glUniform1f(id, x);
}
void opAssign(float x, float y) {
if(id != -1)
glUniform2f(id, x, y);
}
}
static struct UniformsHelper {
@ -17332,8 +17583,9 @@ final class OpenGlShader {
@property Uniform opDispatch(string name)() {
auto i = glGetUniformLocation(_shader.shaderProgram, name.ptr);
if(i == -1)
throw new Exception("Could not find uniform " ~ name);
// FIXME: decide what to do here; the exception is liable to be swallowed by the event syste
//if(i == -1)
//throw new Exception("Could not find uniform " ~ name);
return Uniform(i);
}
}