From c9b6c202209eaaae1a57e5a99d52702e497ba1a4 Mon Sep 17 00:00:00 2001 From: Elias Batek Date: Wed, 27 Dec 2023 17:04:32 +0100 Subject: [PATCH 01/10] Rename PixelRenderer to PixmapRenderer --- pixmappresenter.d | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pixmappresenter.d b/pixmappresenter.d index 0985b69..52dfbe3 100644 --- a/pixmappresenter.d +++ b/pixmappresenter.d @@ -441,7 +441,7 @@ struct WantsOpenGl { } /// -interface PixelRenderer { +interface PixmapRenderer { /++ Does this renderer use OpenGL? @@ -487,7 +487,7 @@ interface PixelRenderer { } /// -final class OpenGL3PixelRenderer : PixelRenderer { +final class OpenGl3PixmapRenderer : PixmapRenderer { private { PresenterObjects* _pro; @@ -748,7 +748,7 @@ final class PixmapPresenter { private { PresenterObjects* _pro; - PixelRenderer _renderer; + PixmapRenderer _renderer; static if (hasTimer) { Timer _timer; @@ -761,14 +761,14 @@ final class PixmapPresenter { /// this(const PresenterConfig config, bool useOpenGl = true) { if (useOpenGl) { - this(config, new OpenGL3PixelRenderer()); + this(config, new OpenGl3PixmapRenderer()); } else { assert(false, "Not implemented"); } } /// - this(const PresenterConfig config, PixelRenderer renderer) { + this(const PresenterConfig config, PixmapRenderer renderer) { _renderer = renderer; // create software framebuffer From 604d806c1e30aad24048d1ba8b147875955d269b Mon Sep 17 00:00:00 2001 From: Elias Batek Date: Wed, 27 Dec 2023 17:06:29 +0100 Subject: [PATCH 02/10] Rename PixelBuffer to Pixmap --- pixmappresenter.d | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pixmappresenter.d b/pixmappresenter.d index 52dfbe3..c4bf41d 100644 --- a/pixmappresenter.d +++ b/pixmappresenter.d @@ -166,7 +166,7 @@ import arsd.simpledisplay; - Additional renderer implementations: - a `ScreenPainter`-based renderer - a legacy OpenGL renderer (maybe) - - Is there something in arsd that serves a similar purpose to `PixelBuffer`? + - Is there something in arsd that serves a similar purpose to `Pixmap`? - Minimum window size - or something similar - to ensure `Scaling.integer` doesn’t break “unexpectedly” @@ -210,7 +210,7 @@ auto ref T typeCast(T, S)(auto ref S v) { /++ Pixel data container +/ -struct PixelBuffer { +struct Pixmap { /// Pixel data Pixel[] data; @@ -428,7 +428,7 @@ struct PresenterConfig { // undocumented struct PresenterObjects { - PixelBuffer framebuffer; + Pixmap framebuffer; SimpleWindow window; PresenterConfig config; } @@ -772,7 +772,7 @@ final class PixmapPresenter { _renderer = renderer; // create software framebuffer - auto framebuffer = PixelBuffer(config.renderer.resolution); + auto framebuffer = Pixmap(config.renderer.resolution); // OpenGL? auto openGlOptions = OpenGlOptions.no; @@ -904,10 +904,13 @@ final class PixmapPresenter { } /// - PixelBuffer framebuffer() @safe pure nothrow @nogc { + Pixmap pixmap() @safe pure nothrow @nogc { return _pro.framebuffer; } + /// ditto + alias framebuffer = pixmap; + /// void reconfigure(const PresenterConfig config) { assert(false, "Not implemented"); From fea3ea1ab0517348f378d4e146b41ae9489c294b Mon Sep 17 00:00:00 2001 From: Elias Batek Date: Wed, 27 Dec 2023 17:07:58 +0100 Subject: [PATCH 03/10] Refactor WantsOpenGl struct --- pixmappresenter.d | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/pixmappresenter.d b/pixmappresenter.d index c4bf41d..3090582 100644 --- a/pixmappresenter.d +++ b/pixmappresenter.d @@ -435,9 +435,16 @@ struct PresenterObjects { /// struct WantsOpenGl { - bool wanted; /// Is OpenGL wanted? - ubyte vMaj; /// major version - ubyte vMin; /// minor version + ubyte vMaj; /// Major version + ubyte vMin; /// Minor version + bool compat; /// Compatibility profile? → true = Compatibility Profile; false = Core Profile + +@safe pure nothrow @nogc: + + /// Is OpenGL wanted? + bool wanted() const { + return vMaj > 0; + } } /// @@ -507,7 +514,7 @@ final class OpenGl3PixmapRenderer : PixmapRenderer { } public WantsOpenGl wantsOpenGl() @safe pure nothrow @nogc { - return WantsOpenGl(true, 3, 0); + return WantsOpenGl(3, 0, false); } // TODO: make this ctor? @@ -779,7 +786,7 @@ final class PixmapPresenter { const openGl = _renderer.wantsOpenGl; if (openGl.wanted) { setOpenGLContextVersion(openGl.vMaj, openGl.vMin); - openGLContextCompatible = false; + openGLContextCompatible = openGl.compat; openGlOptions = OpenGlOptions.yes; } From 5a159c52acf6e13ed59db1c366777bc0891a5da6 Mon Sep 17 00:00:00 2001 From: Elias Batek Date: Wed, 27 Dec 2023 17:22:17 +0100 Subject: [PATCH 04/10] Rename PresenterObjects to PresenterObjectsContainer --- pixmappresenter.d | 88 +++++++++++++++++++++++------------------------ 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/pixmappresenter.d b/pixmappresenter.d index 3090582..1aba896 100644 --- a/pixmappresenter.d +++ b/pixmappresenter.d @@ -427,7 +427,7 @@ struct PresenterConfig { } // undocumented -struct PresenterObjects { +struct PresenterObjectsContainer { Pixmap framebuffer; SimpleWindow window; PresenterConfig config; @@ -470,15 +470,15 @@ interface PixmapRenderer { ) Params: - pro = Pointer to the [PresenterObjects] of the presenter. To be stored for later use. + container = Pointer to the [PresenterObjectsContainer] of the presenter. To be stored for later use. +/ - public void setup(PresenterObjects* pro); + public void setup(PresenterObjectsContainer* container); /++ Reconfigures the renderer Called upon configuration changes. - The new config can be found in the [PresenterObjects] received during `setup()`. + The new config can be found in the [PresenterObjectsContainer] received during `setup()`. +/ public void reconfigure(); @@ -497,7 +497,7 @@ interface PixmapRenderer { final class OpenGl3PixmapRenderer : PixmapRenderer { private { - PresenterObjects* _pro; + PresenterObjectsContainer* _poc; bool _clear = true; @@ -518,15 +518,15 @@ final class OpenGl3PixmapRenderer : PixmapRenderer { } // TODO: make this ctor? - public void setup(PresenterObjects* pro) { - _pro = pro; - _pro.window.visibleForTheFirstTime = &this.visibleForTheFirstTime; - _pro.window.redrawOpenGlScene = &this.redrawOpenGlScene; + public void setup(PresenterObjectsContainer* pro) { + _poc = pro; + _poc.window.visibleForTheFirstTime = &this.visibleForTheFirstTime; + _poc.window.redrawOpenGlScene = &this.redrawOpenGlScene; } private { void visibleForTheFirstTime() { - _pro.window.setAsCurrentOpenGlContext(); + _poc.window.setAsCurrentOpenGlContext(); gl3.loadDynamicLibrary(); this.compileLinkShader(); @@ -538,10 +538,10 @@ final class OpenGl3PixmapRenderer : PixmapRenderer { void redrawOpenGlScene() { if (_clear) { glClearColor( - _pro.config.renderer.background.r, - _pro.config.renderer.background.g, - _pro.config.renderer.background.b, - _pro.config.renderer.background.a + _poc.config.renderer.background.r, + _poc.config.renderer.background.g, + _poc.config.renderer.background.b, + _poc.config.renderer.background.a ); glClear(GL_COLOR_BUFFER_BIT); _clear = false; @@ -553,9 +553,9 @@ final class OpenGl3PixmapRenderer : PixmapRenderer { GL_TEXTURE_2D, 0, 0, 0, - _pro.config.renderer.resolution.width, _pro.config.renderer.resolution.height, + _poc.config.renderer.resolution.width, _poc.config.renderer.resolution.height, GL_RGBA, GL_UNSIGNED_BYTE, - cast(void*) _pro.framebuffer.data.ptr + cast(void*) _poc.framebuffer.data.ptr ); glUseProgram(_shader.shaderProgram); @@ -621,7 +621,7 @@ final class OpenGl3PixmapRenderer : PixmapRenderer { glBindTexture(GL_TEXTURE_2D, _texture); - final switch (_pro.config.renderer.filter) with (ScalingFilter) { + final switch (_poc.config.renderer.filter) with (ScalingFilter) { case nearest: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -638,7 +638,7 @@ final class OpenGl3PixmapRenderer : PixmapRenderer { GL_TEXTURE_2D, 0, GL_RGBA8, - _pro.config.renderer.resolution.width, _pro.config.renderer.resolution.height, + _poc.config.renderer.resolution.width, _poc.config.renderer.resolution.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null @@ -651,56 +651,56 @@ final class OpenGl3PixmapRenderer : PixmapRenderer { public void reconfigure() { Size viewport; - final switch (_pro.config.renderer.scaling) { + final switch (_poc.config.renderer.scaling) { case Scaling.none: - viewport = _pro.config.renderer.resolution; + viewport = _poc.config.renderer.resolution; break; case Scaling.stretch: - viewport = _pro.config.window.size; + viewport = _poc.config.window.size; break; case Scaling.contain: - const float scaleF = karContainScalingFactorF(_pro.config.renderer.resolution, _pro.config.window.size); + const float scaleF = karContainScalingFactorF(_poc.config.renderer.resolution, _poc.config.window.size); viewport = Size( - typeCast!int(scaleF * _pro.config.renderer.resolution.width), - typeCast!int(scaleF * _pro.config.renderer.resolution.height), + typeCast!int(scaleF * _poc.config.renderer.resolution.width), + typeCast!int(scaleF * _poc.config.renderer.resolution.height), ); break; case Scaling.integer: - const int scaleI = karContainScalingFactorInt(_pro.config.renderer.resolution, _pro.config.window.size); - viewport = (_pro.config.renderer.resolution * scaleI); + const int scaleI = karContainScalingFactorInt(_poc.config.renderer.resolution, _poc.config.window.size); + viewport = (_poc.config.renderer.resolution * scaleI); break; case Scaling.integerFP: - if (karContainNeedsDownscaling(_pro.config.renderer.resolution, _pro.config.window.size)) { + if (karContainNeedsDownscaling(_poc.config.renderer.resolution, _poc.config.window.size)) { goto case Scaling.contain; } goto case Scaling.integer; case Scaling.cover: - const float fillF = karCoverScalingFactorF(_pro.config.renderer.resolution, _pro.config.window.size); + const float fillF = karCoverScalingFactorF(_poc.config.renderer.resolution, _poc.config.window.size); viewport = Size( - typeCast!int(fillF * _pro.config.renderer.resolution.width), - typeCast!int(fillF * _pro.config.renderer.resolution.height), + typeCast!int(fillF * _poc.config.renderer.resolution.width), + typeCast!int(fillF * _poc.config.renderer.resolution.height), ); break; } - const Point viewportPos = offsetCenter(viewport, _pro.config.window.size); + const Point viewportPos = offsetCenter(viewport, _poc.config.window.size); glViewport(viewportPos.x, viewportPos.y, viewport.width, viewport.height); this.setupTexture(); _clear = true; } void redrawSchedule() { - _pro.window.redrawOpenGlSceneSoon(); + _poc.window.redrawOpenGlSceneSoon(); } void redrawNow() { - _pro.window.redrawOpenGlSceneNow(); + _poc.window.redrawOpenGlSceneNow(); } private { @@ -754,7 +754,7 @@ struct LoopCtrl { final class PixmapPresenter { private { - PresenterObjects* _pro; + PresenterObjectsContainer* _poc; PixmapRenderer _renderer; static if (hasTimer) { @@ -802,13 +802,13 @@ final class PixmapPresenter { window.windowResized = &this.windowResized; // alloc objects - _pro = new PresenterObjects( + _poc = new PresenterObjectsContainer( framebuffer, window, config, ); - _renderer.setup(_pro); + _renderer.setup(_poc); } } @@ -855,7 +855,7 @@ final class PixmapPresenter { +/ int eventLoop(T...)(long pulseTimeout, void delegate() onPulse, T eventHandlers) { // run event-loop with pulse timer - return _pro.window.eventLoop( + return _poc.window.eventLoop( pulseTimeout, delegate() { onPulse(); this.scheduleRedraw(); }, eventHandlers, @@ -871,7 +871,7 @@ final class PixmapPresenter { int eventLoop(T...)(T eventHandlers) if ( (T.length == 0) || (is(T[0] == delegate) && !is(typeof(() { return T[0](); }()) == LoopCtrl)) ) { - return _pro.window.eventLoop(eventHandlers); + return _poc.window.eventLoop(eventHandlers); } //dfmt on @@ -906,13 +906,13 @@ final class PixmapPresenter { } // run event-loop - return _pro.window.eventLoop(0, eventHandlers); + return _poc.window.eventLoop(0, eventHandlers); } } /// Pixmap pixmap() @safe pure nothrow @nogc { - return _pro.framebuffer; + return _poc.framebuffer; } /// ditto @@ -932,12 +932,12 @@ final class PixmapPresenter { /// bool isFullscreen() { - return _pro.window.fullscreen; + return _poc.window.fullscreen; } /// ditto void isFullscreen(bool enabled) { - return _pro.window.fullscreen = enabled; + return _poc.window.fullscreen = enabled; } /++ @@ -951,14 +951,14 @@ final class PixmapPresenter { ) +/ SimpleWindow tinker() @safe pure nothrow @nogc { - return _pro.window; + return _poc.window; } } // event handlers private { void windowResized(int width, int height) { - _pro.config.window.size = Size(width, height); + _poc.config.window.size = Size(width, height); _renderer.reconfigure(); } } From 2a31c43ad6efbcc6391e481f47f53ac5523fb9ac Mon Sep 17 00:00:00 2001 From: Elias Batek Date: Wed, 27 Dec 2023 17:54:34 +0100 Subject: [PATCH 05/10] Improve pixmappresenter documentation --- pixmappresenter.d | 76 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 18 deletions(-) diff --git a/pixmappresenter.d b/pixmappresenter.d index 1aba896..b97961c 100644 --- a/pixmappresenter.d +++ b/pixmappresenter.d @@ -11,13 +11,14 @@ Think of old-skool games, emulators etc. This library builds upon [arsd.simpledisplay] and [arsd.color]. - It wraps a [arsd.simpledisplay.SimpleWindow|SimpleWindow]) and displays the provided frame data. + It wraps a [arsd.simpledisplay.SimpleWindow|SimpleWindow] and displays the provided frame data. Each frame is automatically centered on, and optionally scaled to, the carrier window. This processing is done with hardware acceleration (OpenGL). Later versions might add a software-mode. Several $(B scaling) modes are supported. - Most notably `keepAspectRatio` that scales frames to the while preserving the original aspect ratio. + Most notably [pixmappresenter.Scaling.contain|contain] that scales pixmaps to the window’s current size + while preserving the original aspect ratio. See [Scaling] for details. $(PITFALL @@ -56,11 +57,11 @@ // Run the eventloop. // The callback delegate will get executed every ~16ms (≙ ~60FPS) and schedule a redraw. presenter.eventLoop(16, delegate() { - // Update the frame(buffer) here… + // Update the pixmap (“framebuffer”) here… // Construct an RGB color value. auto color = Pixel(0x00, 0x00, blueChannel); - // For demo purposes, apply it to the whole framebuffer. + // For demo purposes, apply it to the whole pixmap. presenter.framebuffer.clear(color); // Increment the amount of blue to be used by the next frame. @@ -90,7 +91,7 @@ int main() { // Internal resolution of the images (“frames”) we will render. - // For further details, check out the previous example. + // For further details, check out the “Basic usage” example. const resolution = Size(240, 120); // Configure our presenter in advance. @@ -331,7 +332,7 @@ private @safe pure nothrow @nogc { /++ Scaling/Fit Modes - Each scaling modes has unique behavior for different window-size to frame-size ratios. + Each scaling modes has unique behavior for different window-size to pixmap-size ratios. Unfortunately, there are no universally applicable naming conventions for these modes. In fact, different implementations tend to contradict each other. @@ -339,10 +340,10 @@ private @safe pure nothrow @nogc { $(SMALL_TABLE Mode feature matrix Mode | Aspect Ratio | Pixel Ratio | Cropping | Border | Comment(s) - `none` | preserved | preserved | yes | 4 | Crops if the `window.size < frame.size`. + `none` | preserved | preserved | yes | 4 | Crops if the `window.size < pixmap.size`. `stretch` | no | no | no | none | `contain` | preserved | no | no | 2 | Letterboxing/Pillarboxing - `integer` | preserved | preserved | no | 4 | Works only if `window.size >= frame.size`. + `integer` | preserved | preserved | no | 4 | Works only if `window.size >= pixmap.size`. `integerFP` | preserved | when up | no | 4 or 2 | Hybrid: int upscaling, floating-point downscaling `cover` | preserved | no | yes | none | ) @@ -447,7 +448,11 @@ struct WantsOpenGl { } } -/// +/++ + Renderer abstraction + + A renderer scales, centers and blits pixmaps to screen. + +/ interface PixmapRenderer { /++ Does this renderer use OpenGL? @@ -493,7 +498,9 @@ interface PixmapRenderer { public void redrawNow(); } -/// +/++ + OpenGL 3.0 implementation of a [PixmapRenderer] + +/ final class OpenGl3PixmapRenderer : PixmapRenderer { private { @@ -750,6 +757,10 @@ struct LoopCtrl { } /++ + Pixmap Presenter window + + A high-level window class that displays fully-rendered frames in the form of [Pixmap|Pixmaps]. + The pixmap will be centered and (optionally) scaled. +/ final class PixmapPresenter { @@ -812,7 +823,7 @@ final class PixmapPresenter { } } - // additional convience ctors + // additional convenience ctors public { /// @@ -910,7 +921,11 @@ final class PixmapPresenter { } } - /// + /++ + The [Pixmap] to be presented. + + Use this to “draw” on screen. + +/ Pixmap pixmap() @safe pure nothrow @nogc { return _poc.framebuffer; } @@ -918,30 +933,36 @@ final class PixmapPresenter { /// ditto alias framebuffer = pixmap; - /// + /++ + Updates the configuration of the presenter + +/ void reconfigure(const PresenterConfig config) { assert(false, "Not implemented"); //_framebuffer.size = config.internalResolution; //_renderer.reconfigure(config); } - /// + /++ + Schedules a redraw + +/ void scheduleRedraw() { _renderer.redrawSchedule(); } - /// + /++ + Fullscreen mode + +/ bool isFullscreen() { return _poc.window.fullscreen; } /// ditto void isFullscreen(bool enabled) { - return _poc.window.fullscreen = enabled; + _poc.window.fullscreen = enabled; } /++ - Returns the underlying `SimpleWindow` + Returns the underlying [arsd.simpledisplay.SimpleWindow|SimpleWindow] $(WARNING This is unsupported; use at your own risk. @@ -950,9 +971,28 @@ final class PixmapPresenter { that a presenter or renderer could possibly have set up. ) +/ - SimpleWindow tinker() @safe pure nothrow @nogc { + SimpleWindow tinkerWindow() @safe pure nothrow @nogc { return _poc.window; } + + /++ + Returns the underlying [PixmapRenderer] + + $(TIP + Type-cast the returned reference to the actual implementation type for further use. + ) + + $(WARNING + This is quasi unsupported; use at your own risk. + + Using the result of this function is pratictically no different than + using a reference to the renderer further on after passing it the presenter’s constructor. + It can’t be prohibited but it resembles a footgun. + ) + +/ + PixmapRenderer tinkerRenderer() @safe pure nothrow @nogc { + return _renderer; + } } // event handlers From 48c24ae56d786189d95746e82b13e4a769b9c502 Mon Sep 17 00:00:00 2001 From: Elias Batek Date: Wed, 27 Dec 2023 17:54:59 +0100 Subject: [PATCH 06/10] Add toggleFullscreen() function --- pixmappresenter.d | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pixmappresenter.d b/pixmappresenter.d index b97961c..bf030ac 100644 --- a/pixmappresenter.d +++ b/pixmappresenter.d @@ -938,8 +938,6 @@ final class PixmapPresenter { +/ void reconfigure(const PresenterConfig config) { assert(false, "Not implemented"); - //_framebuffer.size = config.internalResolution; - //_renderer.reconfigure(config); } /++ @@ -961,6 +959,16 @@ final class PixmapPresenter { _poc.window.fullscreen = enabled; } + /++ + Toggles the fullscreen state of the window. + + Turns a non-fullscreen window into fullscreen mode. + Exits fullscreen mode for fullscreen-windows. + +/ + void toggleFullscreen() { + this.isFullscreen = !this.isFullscreen; + } + /++ Returns the underlying [arsd.simpledisplay.SimpleWindow|SimpleWindow] From 115f11a03752cb814c478336cbf20841bc4338e0 Mon Sep 17 00:00:00 2001 From: Elias Batek Date: Wed, 27 Dec 2023 17:55:32 +0100 Subject: [PATCH 07/10] Advertise toggleFullscreen() in example code --- pixmappresenter.d | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pixmappresenter.d b/pixmappresenter.d index bf030ac..597149a 100644 --- a/pixmappresenter.d +++ b/pixmappresenter.d @@ -149,7 +149,7 @@ }, delegate(MouseEvent ev) { // toggle fullscreen mode on double-click if (ev.doubleClick) { - presenter.isFullscreen = !presenter.isFullscreen; + presenter.toggleFullscreen(); } }); } From 0d76beb550292dbd6ee3bc3a57521aa874eebd74 Mon Sep 17 00:00:00 2001 From: Elias Batek Date: Wed, 27 Dec 2023 18:01:34 +0100 Subject: [PATCH 08/10] Use more typeCast!T in pixmappresenter --- pixmappresenter.d | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pixmappresenter.d b/pixmappresenter.d index 597149a..4924b04 100644 --- a/pixmappresenter.d +++ b/pixmappresenter.d @@ -255,7 +255,7 @@ struct Pixmap { int height() inout { if (data.length == 0) return 0; - return (cast(int) data.length / width); + return typeCast!int(data.length / width); } /// Rectangular size of the buffer @@ -265,7 +265,7 @@ struct Pixmap { /// Length of the buffer, i.e. the number of pixels int length() inout { - return cast(int) data.length; + return typeCast!int(data.length); } /++ @@ -325,7 +325,7 @@ private @safe pure nothrow @nogc { Point offsetCenter(const Size drawing, const Size canvas) { auto delta = canvas.deltaPerimeter(drawing); - return (cast(Point) delta) >> 1; + return (typeCast!Point(delta) >> 1); } } @@ -562,7 +562,7 @@ final class OpenGl3PixmapRenderer : PixmapRenderer { 0, 0, _poc.config.renderer.resolution.width, _poc.config.renderer.resolution.height, GL_RGBA, GL_UNSIGNED_BYTE, - cast(void*) _poc.framebuffer.data.ptr + typeCast!(void*)(_poc.framebuffer.data.ptr) ); glUseProgram(_shader.shaderProgram); @@ -617,7 +617,7 @@ final class OpenGl3PixmapRenderer : PixmapRenderer { glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * GLfloat.sizeof, null); glEnableVertexAttribArray(0); - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * GLfloat.sizeof, cast(void*)(2 * GLfloat.sizeof)); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * GLfloat.sizeof, typeCast!(void*)(2 * GLfloat.sizeof)); glEnableVertexAttribArray(1); } From 8f6f14dcfc7a6c92d4f89ea2e83e6b689aabbb7f Mon Sep 17 00:00:00 2001 From: Elias Batek Date: Wed, 27 Dec 2023 18:03:42 +0100 Subject: [PATCH 09/10] Fix length/width/height bugs in Pixmap --- pixmappresenter.d | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pixmappresenter.d b/pixmappresenter.d index 4924b04..188a460 100644 --- a/pixmappresenter.d +++ b/pixmappresenter.d @@ -244,7 +244,7 @@ struct Pixmap { /// ditto void size(int totalPixels, int width) - in (length % width == 0) { + in (totalPixels % width == 0) { data.length = totalPixels; this.width = width; } @@ -253,8 +253,10 @@ struct Pixmap { /// Height of the buffer, i.e. the number of lines int height() inout { - if (data.length == 0) + if (width == 0) { return 0; + } + return typeCast!int(data.length / width); } From 9756f48dee0a46753df740ce9f3f04a052e74e74 Mon Sep 17 00:00:00 2001 From: Elias Batek Date: Wed, 27 Dec 2023 18:19:35 +0100 Subject: [PATCH 10/10] Rename "integerFP" scaling mode to "intHybrid" --- pixmappresenter.d | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/pixmappresenter.d b/pixmappresenter.d index 188a460..2a4fd60 100644 --- a/pixmappresenter.d +++ b/pixmappresenter.d @@ -163,14 +163,16 @@ import arsd.simpledisplay; /* ## TODO - - Complete documentation + - More comprehensive documentation - Additional renderer implementations: - a `ScreenPainter`-based renderer - a legacy OpenGL renderer (maybe) - Is there something in arsd that serves a similar purpose to `Pixmap`? + - Can we convert to/from it? - Minimum window size - - or something similar - to ensure `Scaling.integer` doesn’t break “unexpectedly” + - More control over timing + - that’s a simpledisplay thing, though */ /// @@ -336,8 +338,10 @@ private @safe pure nothrow @nogc { Each scaling modes has unique behavior for different window-size to pixmap-size ratios. - Unfortunately, there are no universally applicable naming conventions for these modes. - In fact, different implementations tend to contradict each other. + $(NOTE + Unfortunately, there are no universally applicable naming conventions for these modes. + In fact, different implementations tend to contradict each other. + ) $(SMALL_TABLE Mode feature matrix @@ -346,10 +350,17 @@ private @safe pure nothrow @nogc { `stretch` | no | no | no | none | `contain` | preserved | no | no | 2 | Letterboxing/Pillarboxing `integer` | preserved | preserved | no | 4 | Works only if `window.size >= pixmap.size`. - `integerFP` | preserved | when up | no | 4 or 2 | Hybrid: int upscaling, floating-point downscaling + `intHybrid` | preserved | when up | no | 4 or 2 | Hybrid: int upscaling, decimal downscaling `cover` | preserved | no | yes | none | ) + $(NOTE + Integer scaling – Note that the resulting integer ratio of a window smaller than a pixmap is `0`. + + Use `intHybrid` to prevent the pixmap from disappearing on disproportionately small window sizes. + It uses $(I integer)-mode for upscaling and the regular $(I contain)-mode for downscaling. + ) + $(SMALL_TABLE Feature | Definition Aspect Ratio | Whether the original aspect ratio (width ÷ height) of the input frame is preserved @@ -367,7 +378,7 @@ enum Scaling { stretch, /// contain, /// integer, /// - integerFP, /// + intHybrid, /// cover, /// // aliases