Mercurial > projects > mde
changeset 50:f68ae1d667f9
Options: impl template & new OptionsFont class.
Options: impl template to ease creating Options sub-classes.
New OptionsFont class (currently only controls render mode and hinting).
committer: Diggory Hardy <diggory.hardy@gmail.com>
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Sun, 01 Jun 2008 18:22:54 +0100 |
parents | bca7e2342d77 |
children | 387a80724c35 |
files | codeDoc/jobs.txt data/L10n/OptionsFont.mtt data/conf/options.mtt mde/Options.d mde/resource/FontTexture.d mde/resource/font.d mde/sdl.d |
diffstat | 7 files changed, 224 insertions(+), 72 deletions(-) [+] |
line wrap: on
line diff
--- a/codeDoc/jobs.txt Sat May 31 13:10:06 2008 +0100 +++ b/codeDoc/jobs.txt Sun Jun 01 18:22:54 2008 +0100 @@ -9,6 +9,7 @@ To do (importance 0-5: 0 pointless, 1 no obvious impact now, 2 todo sometime, 3 useful, 4 important, 5 urgent): Also see todo.txt and FIXME/NOTE comment marks. 5 mergetag crashes with no ending > on a tag and doesn't add header data when comments are included! +4 LCD filtering / fonts from Options. Get yMax for font not all glyphs on line? 4 Not guaranteed to catch up-click ending callback! Appears not to be a problem... 4 OutOfMemoryException is not currently checked for − it should be at least in critical places (use high-level catching of all errors?). 3 on-event draw support (mde.events and GUI need to tell mde.mde) @@ -51,4 +52,5 @@ Done (for git log message): -Enabled getting the size of a text block (before or after rendering). \ No newline at end of file +Options: impl template to ease creating Options sub-classes. +New OptionsFont class (currently only controls render mode and hinting). \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/L10n/OptionsFont.mtt Sun Jun 01 18:22:54 2008 +0100 @@ -0,0 +1,4 @@ +{MT01} +{en-GB} +<entry|lcdFilter=["LCD filtering","Enable or disable LCD filtering. Note that the FreeType library may be compiled with this support disabled due to patents."]> +<entry|renderMode=["Font rendering mode","Controls how fonts are rendered: in gray-scale, or for LCDs (with a horizontal (normal) or vertical layout, with an RGB (normal) or BGR sub-pixel mode."]>
--- a/data/conf/options.mtt Sat May 31 13:10:06 2008 +0100 +++ b/data/conf/options.mtt Sun Jun 01 18:22:54 2008 +0100 @@ -6,6 +6,10 @@ <int|logLevel=1> <double|pollInterval=0.01> +{font} +<int|lcdFilter=0> +<int|hinting=0> + {video} <bool|noFrame=false> <bool|resizable=true>
--- a/mde/Options.d Sat May 31 13:10:06 2008 +0100 +++ b/mde/Options.d Sun Jun 01 18:22:54 2008 +0100 @@ -44,7 +44,8 @@ * Each sub-class provides named variables for maximal-speed reading. Local sub-class references * should be used for reading variables, and via the addOptionsClass() hook will be loaded from * files during pre-init (init0 stage). Do not write changes directly to the subclasses or they will -* not be saved; use, for example, Options.setBool(...). +* not be saved; use, for example, Options.setBool(...). Use an example like OptionsMisc as a +* template for creating a new Options sub-class. * * Details: Options sub-classes hold associative arrays of pointers to all option variables, with a * char[] id. This list is used for saving, loading and to provide generic GUI options screens. The @@ -224,41 +225,174 @@ //END Static //BEGIN Templates - template store(A...) { - alias A a; - } - template decRecurse(char[] A, B...) { - static if (B.length) const char[] decRecurse = A ~ ", " ~ decRecurse!(B); - else const char[] decRecurse = A; - } - template decBool(A...) { - const char[] decBool = "bool " ~ decRecurse!(A) ~ ";\n"; - } - template decInt(A...) { - const char[] decInt = "int " ~ decRecurse!(A) ~ ";\n"; - } - template decDouble(A...) { - const char[] decDouble = "double " ~ decRecurse!(A) ~ ";\n"; + private { + // Return index of first comma, or halts if not found. + template cIndex(char[] A) { + static if (A.length == 0) + static assert (false, "Error in implementation"); + else static if (A[0] == ',') + const size_t cIndex = 0; + else + const size_t cIndex = 1 + cIndex!(A[1..$]); + } + // Return index of first semi-colon, or halts if not found. + template scIndex(char[] A) { + static if (A.length == 0) + static assert (false, "Error: no trailing semi-colon"); + else static if (A[0] == ';') + const size_t scIndex = 0; + else + const size_t scIndex = 1 + scIndex!(A[1..$]); + } + // Look for "type symbols;" in A and return symbols as a comma separated list of names + // (even if type is encountered more than once). Output may contain spaces and, if + // non-empty, will contain a trailing comma. Assumes scIndex always returns less than A.$. + template parseT(char[] type, char[] A) { + static if (A.length < type.length + 1) // end of input, no match + const char[] parseT = ""; + else static if (A[0] == ' ') // leading whitespace: skip + const char[] parseT = parseT!(type, A[1..$]); + else static if (A[0..type.length] == type && A[type.length] == ' ') // match + const char[] parseT = A[type.length+1 .. scIndex!(A)] ~ "," ~ + parseT!(type, A[scIndex!(A)+1 .. $]); + else // no match + const char[] parseT = parseT!(type, A[scIndex!(A)+1 .. $]); + } + // May have a trailing comma. Assumes cIndex always returns less than A.$. + template aaVars(char[] A) { + static if (A.length == 0) + const char[] aaVars = ""; + else static if (A[0] == ' ') + const char[] aaVars = aaVars!(A[1..$]); + else + const char[] aaVars = "\""~A[0..cIndex!(A)]~"\"[]:&"~A[0..cIndex!(A)] ~ "," ~ + aaVars!(A[cIndex!(A)+1..$]); + } + // strip Trailing Comma + template sTC(char[] A) { + static if (A.length && A[$-1] == ',') + const char[] sTC = A[0..$-1]; + else + const char[] sTC = A; + } + // if string is empty (other than space) return null, otherwise enclose: [A] + template listOrNull(char[] A) { + static if (A.length == 0) + const char[] listOrNull = "null"; + else static if (A[0] == ' ') + const char[] listOrNull = listOrNull!(A[1..$]); + else + const char[] listOrNull = "["~A~"]"; + } + } protected { + /** Produces the implementation code to go in the constuctor. */ + template aaDefs(char[] A) { + const char[] aaDefs = + "optsBool = " ~ listOrNull!(sTC!(aaVars!(parseT!("bool" , A)))) ~ ";\n" ~ + "optsInt = " ~ listOrNull!(sTC!(aaVars!(parseT!("int" , A)))) ~ ";\n" ~ + "optsDouble = "~ listOrNull!(sTC!(aaVars!(parseT!("double", A)))) ~ ";\n" ~ + "optsCharA = " ~ listOrNull!(sTC!(aaVars!(parseT!("char[]", A)))) ~ ";\n"; + } + /+/** Produces the implementation code to go in the static constuctor. */ + template optClassAdd(char[] symb) { + const char[] optClassAdd = symb ~ "=new "~classinfo(this).name~";\n";//Options.addOptionsClass("~symb~", );\n"; + }+/ + /** mixin impl("type symbol[, symbol[...]];[type symbol[...];][...]") + * + * E.g. + * --- + * mixin (impl ("bool a, b; int i;")); + * --- + * + * In case this() needs to be customized, mixin(impl!(A)) is equivalent to: + * --- + * mixin (A~"\nthis(){\n"~aaDefs!(A)~"}"); + * --- + * + * Notes: Only use space as whitespace (no new-lines or tabs). Make sure to add a trailing + * semi-colon (;) or you'll get told off! :D + * + * In general errors aren't reported well. Trial with pragma (msg, impl!(...)); if + * necessary. + * + * Extending: mixins could also be used for the static this() {...} or even the whole + * class, but doing would rather decrease readability of any implementation. */ + template impl(char[] A /+, char[] symb+/) { + const char[] impl = A~"\nthis(){\n"~aaDefs!(A)~"}"; + // ~"\nstatic this(){\n"~optClassAdd!(symb)~"}" + } } - template decCharA(A...) { - const char[] decCharA = "char[] " ~ decRecurse!(A) ~ ";\n"; - } - template aaRecurse(char[] A, B...) { - static if (B.length) const char[] aaRecurse = "\""~A~"\"[]:&"~A ~ ", " ~ aaRecurse!(B); - else const char[] aaRecurse = "\""~A~"\"[]:&"~A; - } - template aaBool(A...) { - const char[] aaBool = "optsBool = [" ~ aaRecurse!(A) ~ "];\n"; - } - template aaInt(A...) { - const char[] aaInt = "optsInt = [" ~ aaRecurse!(A) ~ "];\n"; - } - template aaDouble(A...) { - const char[] aaDouble = "optsDouble = [" ~ aaRecurse!(A) ~ "];\n"; - } - template aaCharA(A...) { - const char[] aaCharA = "optsCharA = [" ~ aaRecurse!(A) ~ "];\n"; - } + /+/** mixin impl("type symbol[, symbol[...]];[type symbol[...];][...]") + * + * E.g. + * --- + * mixin (impl ("bool a, b; int i;")); + * --- + * The parser isn't highly accurate. */ + // Try using templates instead? See std.metastrings + static char[] impl (char[] A) { + char[] bools; + char[] ints; + + while (A.length) { + // Trim whitespace + { + size_t i = 0; + while (i < A.length && (A[i] == ' ' || (A[i] >= 9u && A[i] <= 0xD))) + ++i; + A = A[i..$]; + } + if (A.length == 0) break; + + char[] type; + for (size_t i = 0; i < A.length; ++i) { + if (A[i] == ' ' || (A[i] >= 9u && A[i] <= 0xD)) { + type = A[0..i]; + A = A[i+1..$]; + break; + } + } + + char[] symbols; + for (size_t i = 0; i < A.length; ++i) { + if (A[i] == ';') { + symbols = A[0..i]; + A = A[i+1..$]; + break; + } + } + + if (type == "bool") { + if (bools.length) + bools = bools ~ "," ~ symbols; + else + bools = symbols; + } + else if (type == "int") { + if (ints.length) + ints = ints ~ "," ~ symbols; + else + ints = symbols; + } + else { + // Unfortunately, we cannot output non-const strings (even though func is compile-time) + // We also cannot use pragma(msg) because the message gets printed even if the code isn't run. + //pragma(msg, "Warning: impl failed to parse whole input string"); + // Cannot use Cout / logger either. + break; + } + } + + char[] ret; + if (bools.length) + ret = "bool "~bools~";\n"; + if (ints.length) + ret = ret ~ "int "~ints~";\n"; + + + + return ret; + }+/ //END Templates } @@ -341,22 +475,7 @@ /** A home for all miscellaneous options, at least for now. */ OptionsMisc miscOpts; class OptionsMisc : Options { - alias store!("useThreads", "exitImmediately") BOOL; - alias store!("logLevel") INT; - alias store!("pollInterval") DOUBLE; - alias store!("L10n") CHARA; - - mixin (decBool!(BOOL.a)); - mixin (decInt!(INT.a)); - mixin (decDouble!(DOUBLE.a)); - mixin (decCharA!(CHARA.a)); - - this () { - mixin (aaBool!(BOOL.a)); - mixin (aaInt!(INT.a)); - mixin (aaDouble!(DOUBLE.a)); - mixin (aaCharA!(CHARA.a)); - } + mixin (impl!("bool useThreads, exitImmediately; int logLevel; double pollInterval; char[] L10n;")); static this() { miscOpts = new OptionsMisc;
--- a/mde/resource/FontTexture.d Sat May 31 13:10:06 2008 +0100 +++ b/mde/resource/FontTexture.d Sun Jun 01 18:22:54 2008 +0100 @@ -15,12 +15,16 @@ /** Font caching system. * + * This module also serves as the internals to the font module and shouldn't be used except through + * the font module. The two modules could be combined, at a cost to readability. + * * Three types of coordinates get used in the system: FreeType coordinates for each glyph, texture * coordinates, and OpenGL's model/world coordinates (for rendering). The freetype and texture * coords are cartesian (i.e. y increases upwards), although largely this is too abstract to * matter. However, for the model/world coords, y increases downwards. */ module mde.resource.FontTexture; +import mde.Options; import mde.resource.exception; import derelict.freetype.ft; @@ -34,9 +38,6 @@ logger = Log.getLogger ("mde.resource.FontTexture"); } -auto hinting = FT_LOAD_TARGET_NORMAL; //or FT_LOAD_TARGET_LCD (or others) -//FIXME: allow setting lcd filtering -auto lcdFilter = FT_LcdFilter.FT_LCD_FILTER_DEFAULT; //altertives: NONE, LIGHT static const int dimW = 256, dimH = 256; // Texture size const wFactor = 1f / dimW; const hFactor = 1f / dimH; @@ -201,7 +202,8 @@ auto gi = FT_Get_Char_Index (face, chr); auto g = face.glyph; - if (FT_Load_Glyph (face, gi, FT_LOAD_RENDER | hinting)) + // Use renderMode from options, masking bits which are allowable: + if (FT_Load_Glyph (face, gi, FT_LOAD_RENDER | (fontOpts.renderMode & 0xF0000))) throw new fontGlyphException ("Unable to render glyph"); auto b = g.bitmap; @@ -237,9 +239,12 @@ if (b.pixel_mode == FT_Pixel_Mode.FT_PIXEL_MODE_GRAY && b.num_grays == 256) format = GL_LUMINANCE; else if (b.pixel_mode == FT_Pixel_Mode.FT_PIXEL_MODE_LCD || - b.pixel_mode == FT_Pixel_Mode.FT_PIXEL_MODE_LCD_V) - format = GL_RGB; - else + b.pixel_mode == FT_Pixel_Mode.FT_PIXEL_MODE_LCD_V) { + if (fontOpts.renderMode & RENDER_LCD_BGR) + format = GL_BGR; + else + format = GL_RGB; + } else throw new fontGlyphException ("Unsupported freetype bitmap format"); glTexSubImage2D(GL_TEXTURE_2D, 0, @@ -374,6 +379,24 @@ int nextYPos = 0; // y position for next created line (0 for first line) } +// this bit of renderMode, if set, means read glyph as BGR not RGB when using LCD rendering +enum { RENDER_LCD_BGR = 1 << 30 } +OptionsFont fontOpts; +class OptionsFont : Options { + /* renderMode should be FT_LOAD_TARGET_NORMAL, FT_LOAD_TARGET_LIGHT, FT_LOAD_TARGET_LCD or + * FT_LOAD_TARGET_LCD_V, possibly with bit 31 set (see RENDER_LCD_BGR). + * FT_LOAD_TARGET_MONO is unsupported. + * + * lcdFilter should come from enum FT_LcdFilter: + * FT_LCD_FILTER_NONE, FT_LCD_FILTER_DEFAULT, FT_LCD_FILTER_LIGHT */ + mixin (impl!("int renderMode, lcdFilter;")); + + static this() { + fontOpts = new OptionsFont; + Options.addOptionsClass (fontOpts, "font"); + } +} + struct GlyphAttribs { int x, y; // position within texture int w, h; // bitmap size
--- a/mde/resource/font.d Sat May 31 13:10:06 2008 +0100 +++ b/mde/resource/font.d Sun Jun 01 18:22:54 2008 +0100 @@ -16,6 +16,7 @@ /// Sets up freetype (in a basic way). module mde.resource.font; +import mde.Options; import mde.resource.FontTexture; import mde.resource.exception; @@ -70,6 +71,14 @@ logger.warn (logger.format (tmp, "Using an untested FreeType version: {}.{}.{}", maj, min, patch)); } + // Set LCD filtering method + if (FT_Library_SetLcdFilter(library, cast(FT_LcdFilter)fontOpts.lcdFilter)) { + // If setting failed, leave at default (disabled status). Note: it is disabled by + // default because the code isn't compiled in by default, to avoid patents. + logger.warn ("Bad/unsupported LCD filter option; disabling."); + Options.setInt ("font", "lcdFilter", FT_LcdFilter.FT_LCD_FILTER_NONE); + } + /* Load font settings * * Each mergetag section corresponds to a font; each is loaded whether used or not @@ -260,3 +269,6 @@ FT_Face face; } + +/+class OptionsFont : Options { + alias store!(+/ \ No newline at end of file
--- a/mde/sdl.d Sat May 31 13:10:06 2008 +0100 +++ b/mde/sdl.d Sun Jun 01 18:22:54 2008 +0100 @@ -179,19 +179,7 @@ /** All video options. */ OptionsVideo vidOpts; class OptionsVideo : Options { - alias store!("fullscreen","hardware","resizable","noFrame") BOOL; - alias store!("screenW","screenH","windowW","windowH") INT; - //alias store!() CHARA; - - mixin (decBool!(BOOL.a)); - mixin (decInt!(INT.a)); - //mixin (decCharA!(CHARA.a)); - - this () { - mixin (aaBool!(BOOL.a)); - mixin (aaInt!(INT.a)); - //mixin (aaCharA!(CHARA.a)); - } + mixin (impl!("bool fullscreen,hardware,resizable,noFrame; int screenW,screenH,windowW,windowH;")); static this() { vidOpts = new OptionsVideo;