2 * Pixel Graphics Library
3 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
4 * Understanding is not required. Only obedience.
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, version 3 of the License ONLY.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 module iv
.sdpy
.core
/*is aliced*/;
21 import iv
.sdpy
.compat
;
22 private import iv
.sdpy
.gfxbuf
;
25 // ////////////////////////////////////////////////////////////////////////// //
26 /// generic VideoLib exception
27 class VideoLibError
: Exception
{
28 static if (__VERSION__
> 2067) {
29 this (string msg
, string file
=__FILE__
, usize line
=__LINE__
, Throwable next
=null) @safe pure nothrow @nogc {
30 super(msg
, file
, line
, next
);
33 this (string msg
, string file
=__FILE__
, usize line
=__LINE__
, Throwable next
=null) @safe pure nothrow {
34 super(msg
, file
, line
, next
);
40 // ////////////////////////////////////////////////////////////////////////// //
49 /// is videolib initialized?
52 Partial
, /// not fully (i.e. `vlInit()` failed in the middle)
53 Done
/// completely initialized
57 // ////////////////////////////////////////////////////////////////////////// //
58 private shared VLInitState pvInited
= VLInitState
.Not
;
60 /// is VideoLib properly initialized and videomode set?
61 @property VLInitState
vlInitState () @trusted nothrow @nogc {
62 import core
.atomic
: atomicLoad
;
63 return atomicLoad(pvInited
);
66 // ////////////////////////////////////////////////////////////////////////// //
67 shared bool vlMag2x
= true; // set to true to create double-sized window; default: true; have no effect after calling vlInit()
68 shared bool vlScanlines
= true; // set to true to use 'scanline' filter in mag2x mode; default: true
69 shared VLFilter vlFilter
= VLFilter
.None
; /// output filter; default: VLFilter.None
71 __gshared
bool sdpyUseOpenGL
= false;
72 __gshared
bool sdpyShowFPS
= true;
73 __gshared
uint sdpyFPS
= 35;
74 __gshared string sdpyWindowTitle
= "SDPY";
77 // ////////////////////////////////////////////////////////////////////////// //
78 /// screen dimensions, should be changed prior to calling vlInit()
79 private shared uint vsWidth
= 320;
80 private shared uint vsHeight
= 240;
82 /// get current screen width
83 @gcc_inline @property uint vlWidth() () @trusted nothrow @nogc {
84 //static if (__VERSION__ > 2067) pragma(inline, true);
85 import core
.atomic
: atomicLoad
;
86 return atomicLoad(vsWidth
);
89 /// get current screen height
90 @gcc_inline @property uint vlHeight() () @trusted nothrow @nogc {
91 //static if (__VERSION__ > 2067) pragma(inline, true);
92 import core
.atomic
: atomicLoad
;
93 return atomicLoad(vsHeight
);
96 /// set screen width; must be used before vlInit()
97 @property void vlWidth (int wdt
) @trusted {
98 import core
.atomic
: atomicLoad
, atomicStore
;
99 if (atomicLoad(pvInited
) != VLInitState
.Not
) throw new VideoLibError("trying to change screen width after initialization");
100 if (wdt
< 1 || wdt
> 8192) throw new VideoLibError("invalid screen width");
101 atomicStore(vsWidth
, wdt
);
104 /// set screen height; must be used before vlInit()
105 @property void vlHeight (int hgt
) @trusted {
106 import core
.atomic
: atomicLoad
, atomicStore
;
107 if (atomicLoad(pvInited
) != VLInitState
.Not
) throw new VideoLibError("trying to change screen height after initialization");
108 if (hgt
< 1 || hgt
> 8192) throw new VideoLibError("invalid screen height");
109 atomicStore(vsHeight
, hgt
);
112 __gshared
uint* vlVScr
= null; /// current SDL 'virtual screen', ARGB format for LE
113 private __gshared
uint* vscr2x
= null; // this is used in magnifying blitters
115 /// build `vscr2x` if necessary, return buffer
116 @property uint[] vlBuildBuffer2Blit () @trusted nothrow @nogc { return vlPaintFrameDefault(); }
119 private __gshared
int effectiveMag2x
; // effective vlMag2x (1, 2)
120 private __gshared
int prevLogSizeWas1x
= 0; // DON'T change to bool!
122 @gcc_inline @property int vlEffectiveWidth() () nothrow @trusted @nogc {
123 //static if (__VERSION__ > 2067) pragma(inline, true);
124 import core
.atomic
: atomicLoad
;
125 return cast(int)atomicLoad(vsWidth
)*effectiveMag2x
;
127 @gcc_inline @property int vlEffectiveHeight() () nothrow @trusted @nogc {
128 //static if (__VERSION__ > 2067) pragma(inline, true);
129 import core
.atomic
: atomicLoad
;
130 return cast(int)atomicLoad(vsHeight
)*effectiveMag2x
;
132 @gcc_inline @property int vlEffectiveMag() () nothrow @trusted @nogc {
133 //static if (__VERSION__ > 2067) pragma(inline, true);
134 return effectiveMag2x
;
138 // ////////////////////////////////////////////////////////////////////////// //
140 * Process CLI args from main().
143 * args = command line arguments
146 * command line with processed arguments removed
148 void vlProcessArgs (ref string
[] args
) @trusted nothrow {
150 for (uint idx
= 1; idx
< args
.length
; ++idx
) {
151 auto arg
= args
[idx
];
152 if (arg
== "--") break;
153 if (arg
.length
< 3 || arg
[0] != '-' || arg
[1] != '-') continue;
155 if (arg
.length
> 5 && arg
[2..5] == "no-") {
162 case "tv": vlScanlines
= yes
; break;
163 case "bw": if (yes
) vlFilter
= VLFilter
.BlackNWhite
; break;
164 case "green": if (yes
) vlFilter
= VLFilter
.Green
; break;
165 case "1x": vlMag2x
= !yes
; break;
166 case "2x": vlMag2x
= yes
; break;
167 case "x11": sdpyUseOpenGL
= !yes
; break;
168 case "ogl": sdpyUseOpenGL
= yes
; break;
169 case "fps": sdpyShowFPS
= yes
; break;
172 import core
.stdc
.stdlib
: exit
;
173 import core
.stdc
.stdio
: stderr
, fprintf
;
175 "video options (add \"no-\" to negate):\n"~
176 " --tv scanlines filter\n"~
177 " --bw black-and-white filter\n"~
178 " --green green filter\n"~
179 " --1x normal size\n"~
181 " --x11 use X11 backend (default)\n"~
182 " --ogl use OpenGl backend\n"~
183 " --fps show FPS (default)\n"~
184 " --no-fps don't show FPS\n"
189 default: continue main_loop
;
192 foreach (immutable c
; idx
..args
.length
-1) args
[c
] = args
[c
+1];
194 --idx
; // compensate for removed element
200 * Initialize buffers.
206 * VideoLibError on error
208 void vlInit () @trusted {
209 import core
.atomic
: atomicLoad
, atomicStore
;
211 final switch (atomicLoad(pvInited
)) with (VLInitState
) {
213 case Partial
: throw new VideoLibError("can't continue initialization");
217 if (vsWidth
< 1 || vsHeight
< 1) throw new VideoLibError("sdpy: screen size is not set");
219 import core
.exception
: onOutOfMemoryError
;
220 import core
.stdc
.stdlib
: malloc
, free
;
224 effectiveMag2x
= (vlMag2x ?
2 : 1);
225 prevLogSizeWas1x
= 1;
227 if (vlVScr
!is null) free(vlVScr
);
228 vlVScr
= cast(uint*)malloc(vsWidth
*vsHeight
*vlVScr
[0].sizeof
);
229 if (vlVScr
is null) onOutOfMemoryError();
231 if (vscr2x
!is null) free(vscr2x
);
232 vscr2x
= cast(uint*)malloc(vsWidth
*effectiveMag2x
*vsHeight
*effectiveMag2x
*vscr2x
[0].sizeof
);
233 if (vscr2x
is null) onOutOfMemoryError();
235 atomicStore(pvInited
, VLInitState
.Partial
);
237 atomicStore(pvInited
, VLInitState
.Done
);
242 * Deinitialize, free resources.
250 private void vlDeinitInternal () /*@trusted nothrow @nogc*/ {
251 import core
.atomic
: atomicLoad
, atomicStore
;
252 import core
.stdc
.stdlib
: free
;
254 if (atomicLoad(pvInited
) == VLInitState
.Not
) return;
256 if (vlVScr
!is null) { free(vlVScr
); vlVScr
= null; }
257 if (vscr2x
!is null) { free(vscr2x
); vscr2x
= null; }
260 atomicStore(pvInited
, VLInitState
.Not
);
265 * Shutdown, free resources. You don't need to call this explicitely.
273 void vlDeinit () /*@trusted nothrow @nogc*/ {
274 import core
.atomic
: atomicLoad
;
275 if (atomicLoad(pvInited
) != VLInitState
.Not
) {
281 // ////////////////////////////////////////////////////////////////////////// //
282 private enum buildBlit1x(string name
, string op
, string wrt
) =
283 `private void `~name
~` () @trusted nothrow @nogc {`~
284 ` auto s = cast(const(ubyte)*)vlVScr;`~
285 ` auto d = cast(ubyte*)vscr2x;`~
286 ` foreach (immutable _; 0..vsWidth*vsHeight) {`~
287 ` ubyte i = `~op
~`;`~
295 mixin(buildBlit1x
!("blit1xBW", "(s[0]*28+s[1]*151+s[2]*77)/256", "d[0] = d[1] = d[2] = i"));
296 mixin(buildBlit1x
!("blit1xGreen", "(s[0]*28+s[1]*151+s[2]*77)/256", "d[0] = d[2] = 0; d[1] = i"));
299 private enum buildBlit2x(string name
, string op
) =
300 `private void `~name
~` () @trusted nothrow @nogc {`~
301 ` auto s = cast(const(ubyte)*)vlVScr;`~
302 ` auto d = cast(uint*)vscr2x;`~
303 ` immutable auto wdt = vsWidth;`~
304 ` immutable auto wdt2x = vsWidth*2;`~
305 ` foreach (immutable y; 0..vsHeight) {`~
306 ` foreach (immutable x; 0..wdt) {`~
308 ` immutable uint c1 = ((((c0&0x00ff00ff)*6)>>3)&0x00ff00ff)|(((c0&0x0000ff00)*6)>>3)&0x0000ff00;`~
309 ` d[0] = d[1] = c0;`~
310 ` d[wdt2x] = d[wdt2x+1] = c1;`~
314 // fix d: skip one scanline
320 mixin(buildBlit2x
!("blit2xTV", "immutable uint c0 = (cast(immutable(uint)*)s)[0];"));
321 mixin(buildBlit2x
!("blit2xTVBW", "immutable ubyte i = cast(ubyte)((s[0]*28+s[1]*151+s[2]*77)/256); immutable uint c0 = (i<<16)|(i<<8)|i;"));
322 mixin(buildBlit2x
!("blit2xTVGreen", "immutable ubyte i = cast(ubyte)((s[0]*28+s[1]*151+s[2]*77)/256); immutable uint c0 = i<<8;"));
325 // ////////////////////////////////////////////////////////////////////////// //
327 * Paint virtual screen onto blit buffer.
335 private uint[] vlPaintFrameDefault () @trusted nothrow @nogc {
336 import core
.atomic
: atomicLoad
;
337 // fix 'logical size'
338 immutable flt
= atomicLoad(vlFilter
);
339 immutable scanln
= atomicLoad(vlScanlines
);
340 if (effectiveMag2x
== 2 && scanln
) {
341 // mag2x and scanlines: size is 2x
342 if (prevLogSizeWas1x
) {
343 prevLogSizeWas1x
= 0;
346 // any other case: size is 2x
347 if (!prevLogSizeWas1x
) {
348 prevLogSizeWas1x
= 1;
351 // apply filters if any
352 if (effectiveMag2x
== 2 && scanln
) {
353 // heavy case: scanline filter turned on
354 final switch (flt
) with (VLFilter
) {
355 case None
: blit2xTV(); break;
356 case BlackNWhite
: blit2xTVBW(); break;
357 case Green
: blit2xTVGreen(); break;
359 return vscr2x
[0..(vsHeight
*2)*(vsWidth
*2)*vscr2x
[0].sizeof
];
362 if (flt
== VLFilter
.None
) {
364 return vlVScr
[0..vsHeight
*vsWidth
*vlVScr
[0].sizeof
];
366 import core
.stdc
.string
: memcpy
;
367 final switch (flt
) with (VLFilter
) {
368 case None
: memcpy(vscr2x
, vlVScr
, vsWidth
*vsHeight
*vlVScr
[0].sizeof
); break; // just in case
369 case BlackNWhite
: blit1xBW(); break;
370 case Green
: blit1xGreen(); break;
372 return vscr2x
[0..vsHeight
*vsWidth
*vscr2x
[0].sizeof
];
378 // ////////////////////////////////////////////////////////////////////////// //
379 shared static ~this () {