diff --git a/h2d/Text.hx b/h2d/Text.hx index f6d1f7205..a9806dc10 100644 --- a/h2d/Text.hx +++ b/h2d/Text.hx @@ -111,6 +111,18 @@ class Text extends Drawable { Allow line break. **/ public var lineBreak(default,set) : Bool = true; + /** + Allow word wrapping. + **/ + public var wordWrap(default,set) : Bool = true; + /** + Highlight RGB color. Alpha value is ignored. + **/ + public var highlightColor : Int = 0xFFFFFF; + /** + Tag for indicating highlighted text. + **/ + public var highlightTag : String = ""; var glyphs : TileGroup; var needsRebuild : Bool; @@ -201,6 +213,13 @@ class Text extends Drawable { return b; } + function set_wordWrap(b) { + if( wordWrap == b ) return b; + wordWrap = b; + rebuild(); + return b; + } + override function constraintSize(width:Float, height:Float) { constraintWidth = width; updateConstraint(); @@ -329,20 +348,49 @@ class Text extends Drawable { var lines = [], restPos = 0; var x = leftMargin; var wLastSep = 0.; - for( i in 0...text.length ) { + var skipCount = 0; + + final nonBreakingSpaceTag = ' '; + final textSize = if( StringTools.contains(text, nonBreakingSpaceTag) ) { + StringTools.replace(text, nonBreakingSpaceTag, ' ').length; + } else { + text.length; + }; + for( i in 0...textSize ) { var cc = StringTools.fastCodeAt(text, i); + var remaining = text.substr(i); + + if( skipCount > 0 ) { + skipCount--; + continue; + } + + if( highlightTag != null && highlightTag != "" && StringTools.startsWith(remaining, highlightTag) ) { + skipCount = highlightTag.length - 1; + continue; + } + + var isNonBreakingSpace = StringTools.startsWith(remaining, nonBreakingSpaceTag); + if( isNonBreakingSpace ) { + var nbspTagSize = nonBreakingSpaceTag.length; + var preceding = text.substr(0, i + nbspTagSize); + var exceedingNbsp = remaining.substr(nbspTagSize); + text = StringTools.replace(preceding, nonBreakingSpaceTag, ' ') + exceedingNbsp; + cc = ' '.code; + } + var e = font.getChar(cc); var newline = cc == '\n'.code; var esize = e.width + e.getKerningOffset(prevChar); - var isComplement = (i < text.length - 1 && font.charset.isComplementChar(StringTools.fastCodeAt(text, i + 1))); - if( font.charset.isBreakChar(cc) && !isComplement ) { + var isComplement = (i < textSize - 1 && font.charset.isComplementChar(StringTools.fastCodeAt(text, i + 1))); + if( font.charset.isBreakChar(cc) && !isNonBreakingSpace && !isComplement ) { if( lines.length == 0 && leftMargin > 0 && x > maxWidth ) { lines.push(""); if ( sizes != null ) sizes.push(leftMargin); x -= leftMargin; } var size = x + esize + letterSpacing; /* TODO : no letter spacing */ - var k = i + 1, max = text.length; + var k = i + 1, max = textSize; var prevChar = cc; var breakFound = false; while( size <= maxWidth && k < max ) { @@ -354,8 +402,8 @@ class Text extends Drawable { var e = font.getChar(cc); size += e.width + letterSpacing + e.getKerningOffset(prevChar); prevChar = cc; - if ( font.charset.isBreakChar(cc) ) { - if ( k >= text.length ) + if ( font.charset.isBreakChar(cc) && !isNonBreakingSpace ) { + if ( k >= textSize ) break; var nc = StringTools.fastCodeAt(text, k); if ( !font.charset.isComplementChar(nc) ) break; @@ -373,10 +421,11 @@ class Text extends Drawable { } else wLastSep = size; } - else if( (x + esize + letterSpacing) - wLastSep > maxWidth && lineBreak ) { + else if( wordWrap && (x + esize + letterSpacing) - wLastSep > (maxWidth - esize) ) { newline = true; lines.push(text.substr(restPos, i - restPos)); - restPos = font.charset.isSpace(cc) ? i + 1 : i; + restPos = i; + x -= esize + letterSpacing; } if( e != null && cc != '\n'.code ) x += esize + letterSpacing; @@ -388,13 +437,13 @@ class Text extends Drawable { } else prevChar = cc; } - if( restPos < text.length ) { + if( restPos < textSize ) { if( lines.length == 0 && leftMargin > 0 && x + afterData - letterSpacing > maxWidth ) { lines.push(""); if ( sizes != null ) sizes.push(leftMargin); x -= leftMargin; } - lines.push(text.substr(restPos, text.length - restPos)); + lines.push(text.substr(restPos, textSize - restPos)); if ( sizes != null ) sizes.push(x); } return lines.join("\n"); @@ -437,9 +486,26 @@ class Text extends Drawable { var colors = colorSegments; var colorsPos = 0; if( colors != null && colors.length == 0 ) colors = null; - if( rebuild ) glyphs.setDefaultColor(0xFFFFFF); + if( rebuild ) glyphs.setDefaultColor(textColor); + var isHighlight = false; + var skipCount = 0; for( i in 0...t.length ) { var cc = StringTools.fastCodeAt(t, i); + var remaining = t.substr(i); + + if( skipCount > 0 ) { + skipCount--; + continue; + } + + if( highlightTag != null && highlightTag != "" && StringTools.startsWith(remaining, highlightTag) ) { + isHighlight = !isHighlight; + if( rebuild && colors == null ) + glyphs.setDefaultColor(isHighlight ? highlightColor : textColor); + skipCount = highlightTag.length - 1; + continue; + } + var e = font.getChar(cc); var offs = e.getKerningOffset(prevChar); var esize = e.width + offs; @@ -522,16 +588,13 @@ class Text extends Drawable { function set_textColor(c) { if( this.textColor == c ) return c; this.textColor = c; - var a = color.w; - color.setColor(c); - color.w = a; + rebuild(); return c; } /** Set the text color segments. This is an Array containing a pair of (position,color). Each time the text display will reach the given position, the color will be set. - The segment color is multiplied by the global textColor. **/ public function setColorSegments( arr ) { colorSegments = arr; diff --git a/h3d/impl/GlDriver.hx b/h3d/impl/GlDriver.hx index 835a41a7c..e8197945d 100644 --- a/h3d/impl/GlDriver.hx +++ b/h3d/impl/GlDriver.hx @@ -1029,7 +1029,7 @@ class GlDriver extends Driver { case GL.RGB10_A2: GL.RGBA; case GL.RED, GL.R8, GL.R16F, GL.R32F, 0x822A: GL.RED; case GL.RG, GL.RG8, GL.RG16F, GL.RG32F, 0x822C: GL.RG; - case GL.RGB16F, GL.RGB32F, 0x8054, 0x8E8F, 0x8D64: GL.RGB; + case GL.RGB16F, GL.RGB32F, 0x8054, 0x8E8F, 0x8D64, 0x9274: GL.RGB; case 0x805B, 0x83F1, // DXT1 0x83F2, // DXT3 @@ -1050,7 +1050,7 @@ class GlDriver extends Driver { case R8, RG8, RGB8, R16F, RG16F, RGB16F, R32F, RG32F, RGB32F, RG11B10UF, RGB10A2: #if js glES >= 3 #else true #end; case S3TC(n): n <= maxCompressedTexturesSupport; case ASTC(10): #if js textureSupport != null && textureSupport.astc #else true #end; - case ETC(0): #if js textureSupport != null && textureSupport.etc1 #else true #end; + case ETC(0): #if js textureSupport != null && (textureSupport.etc1 || textureSupport.etc2) #else true #end; case ETC(1), ETC(2): #if js textureSupport != null && textureSupport.etc2 #else true #end; default: false; } @@ -1142,32 +1142,39 @@ class GlDriver extends Driver { case RG11B10UF: tt.internalFmt = GL.R11F_G11F_B10F; tt.pixelFmt = GL.UNSIGNED_INT_10F_11F_11F_REV; - case S3TC(n) if( n <= maxCompressedTexturesSupport ): - if( t.width&3 != 0 || t.height&3 != 0 ) - throw "Compressed texture "+t+" has size "+t.width+"x"+t.height+" - must be a multiple of 4"; - switch( n ) { - case 1: tt.internalFmt = 0x83F1; // COMPRESSED_RGBA_S3TC_DXT1_EXT - case 2: tt.internalFmt = 0x83F2; // COMPRESSED_RGBA_S3TC_DXT3_EXT - case 3: tt.internalFmt = 0x83F3; // COMPRESSED_RGBA_S3TC_DXT5_EXT - case 6: tt.internalFmt = 0x8E8F; // COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT - case 7: tt.internalFmt = 0x8E8C; // COMPRESSED_RGBA_BPTC_UNORM - default: throw "Unsupported texture format "+t.format; - } - case ASTC(n): - if( t.width&3 != 0 || t.height&3 != 0 ) - throw "Compressed texture "+t+" has size "+t.width+"x"+t.height+" - must be a multiple of 4"; - switch( n ) { - case 10: tt.internalFmt = 0x93B0; // COMPRESSED_RGBA_ASTC_4x4_KHR - default: throw "Unsupported texture format "+t.format; - } - case ETC(n): - if( t.width&3 != 0 || t.height&3 != 0 ) - throw "Compressed texture "+t+" has size "+t.width+"x"+t.height+" - must be a multiple of 4"; - switch( n ) { - case 0: tt.internalFmt = 0x8D64; // ETC1 - case 1, 2: tt.internalFmt = 0x9278; // ETC2 RGBA8 - default: throw "Unsupported texture format "+t.format; - } + case S3TC(n) if( n <= maxCompressedTexturesSupport ): + if( t.width&3 != 0 || t.height&3 != 0 ) + throw "Compressed texture "+t+" has size "+t.width+"x"+t.height+" - must be a multiple of 4"; + switch( n ) { + case 1: tt.internalFmt = 0x83F1; // COMPRESSED_RGBA_S3TC_DXT1_EXT + case 2: tt.internalFmt = 0x83F2; // COMPRESSED_RGBA_S3TC_DXT3_EXT + case 3: tt.internalFmt = 0x83F3; // COMPRESSED_RGBA_S3TC_DXT5_EXT + case 6: tt.internalFmt = 0x8E8F; // COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT + case 7: tt.internalFmt = 0x8E8C; // COMPRESSED_RGBA_BPTC_UNORM + default: throw "Unsupported texture format "+t.format; + } + case ASTC(n): + if( t.width&3 != 0 || t.height&3 != 0 ) + throw "Compressed texture "+t+" has size "+t.width+"x"+t.height+" - must be a multiple of 4"; + switch( n ) { + case 10: tt.internalFmt = 0x93B0; // COMPRESSED_RGBA_ASTC_4x4_KHR + default: throw "Unsupported texture format "+t.format; + } + case ETC(n): + if( t.width&3 != 0 || t.height&3 != 0 ) + throw "Compressed texture "+t+" has size "+t.width+"x"+t.height+" - must be a multiple of 4"; + switch( n ) { + case 0: + #if js + // WEBGL_compressed_texture_etc1 is not available in WebGL2; use ETC2 RGB8 fallback. + if( textureSupport != null && !textureSupport.etc1 && textureSupport.etc2 ) + tt.internalFmt = 0x9274; // ETC2 RGB8 + else + #end + tt.internalFmt = 0x8D64; // ETC1 + case 1, 2: tt.internalFmt = 0x9278; // ETC2 RGBA8 + default: throw "Unsupported texture format "+t.format; + } default: throw "Unsupported texture format "+t.format; } diff --git a/haxelib.json b/haxelib.json index c13326918..409351970 100644 --- a/haxelib.json +++ b/haxelib.json @@ -3,7 +3,7 @@ "url": "http://heaps.io", "license": "BSD", "description": "The GPU Game Framework", - "version": "2.1.13", + "version": "2.1.14", "releasenote": "See CHANGELOG.md", "contributors": [ "ncannasse" diff --git a/hxd/res/Ktx2.hx b/hxd/res/Ktx2.hx index 99f389732..5276b4fb2 100644 --- a/hxd/res/Ktx2.hx +++ b/hxd/res/Ktx2.hx @@ -461,7 +461,6 @@ class Ktx2Decoder { final workerScript = ' let config; let transcoderPending; - let formatInfo; let BasisModule; // Inject the basis transcoder script into the workers context ${_transcoderScript} @@ -474,10 +473,9 @@ class Ktx2Decoder { init(message.transcoderBinary); break; case "transcode": - formatInfo = message.formatInfo; transcoderPending.then(function() { try { - const { faces, buffers, width, height, hasAlpha, format, type, dfdFlags } = transcode( message.buffer ); + const { faces, buffers, width, height, hasAlpha, format, type, dfdFlags } = transcode( message.buffer, message.formatInfo ); self.postMessage( { type: "transcode", id: message.id, data: { faces, width, height, hasAlpha, format, type, dfdFlags } }, buffers ); } catch (error) { self.postMessage({ type: "error", id: message.id, error: error.message }); @@ -514,7 +512,7 @@ class Ktx2Decoder { return result; } - function transcode(buffer) { + function transcode(buffer, formatInfo) { let ktx2File = new BasisModule.KTX2File(new Uint8Array(buffer)); function cleanup() { ktx2File.close(); diff --git a/hxsl/Dce.hx b/hxsl/Dce.hx index 316608c47..8e69fb2cf 100644 --- a/hxsl/Dce.hx +++ b/hxsl/Dce.hx @@ -63,6 +63,7 @@ class Dce { var channelVars : Array; var markAsKeep : Bool; var checkBranchesFun : TExpr -> Void; + var fragDepthId = Tools.allocVarId(); public function new() { checkBranchesFun = this.checkBranches; // prevent recreation of instance closure @@ -204,6 +205,27 @@ class Dce { link(v, writeTo); case TSwiz({ e : TVar(v) }, swiz): link(v, writeTo, swizBits(swiz)); + case TBinop(op, { e : TGlobal(FragDepth) }, e2 ): + var v:TVar = { + id: fragDepthId, + name: "FragDepth", + type: TFloat, + kind: Global, + }; + var v = get(v); + switch( op ) { + case OpAssign: + // Last assign will always clear all other dependencies. + v.adeps = []; + v.deps.clear(); + case OpAssignOp(_): + default: + return; + } + v.keep = 15; + writeTo.push(v, 15); + check(e2, writeTo, isAffected); + writeTo.pop(); case TBinop(OpAssign | OpAssignOp(_), { e : TVar(v) }, e): var v = get(v); writeTo.push(v,15); @@ -344,6 +366,8 @@ class Dce { count++; } return { e : TBlock(out), p : e.p, t : e.t }; + case TBinop(OpAssign | OpAssignOp(_), { e : TGlobal(FragDepth) }, { e : TVar(v) }) if( get(v).used == 0 ): + return { e : TConst(CNull), t : e.t, p : e.p }; case TVarDecl(v,e2) | TBinop(OpAssign | OpAssignOp(_), { e : (TVar(v) | TSwiz( { e : TVar(v) }, _) | TArray( { e : TVar(v) }, _)) }, e2) if( get(v).used == 0 ): return (e2 != null && e2.hasSideEffect()) ? mapExpr(e2, false) : { e : TConst(CNull), t : e.t, p : e.p }; case TBinop(OpAssign | OpAssignOp(_), { e : TSwiz( { e : TVar(v) }, swiz) }, e2) if( get(v).used & swizBits(swiz) == 0 ): @@ -399,4 +423,4 @@ class Dce { } } -} \ No newline at end of file +} diff --git a/package.json b/package.json index 9fe299d9e..27e281a52 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@hacksawstudios/heaps", - "version": "2.1.13", + "version": "2.1.14", "description": "_High Performance Game Framework_", "main": "index.js", "devDependencies": {