diff --git a/flixel/text/FlxBitmapText.hx b/flixel/text/FlxBitmapText.hx index 6fcec9169a..4d0ec1551b 100644 --- a/flixel/text/FlxBitmapText.hx +++ b/flixel/text/FlxBitmapText.hx @@ -152,12 +152,18 @@ class FlxBitmapText extends FlxSprite * NOTE: If the borderSize is 1, borderQuality of 0 or 1 will have the exact same effect (and performance). */ public var borderQuality(default, set):Float = 0; - + + /** + * Internal handler for deprecated `shadowOffset` field + */ + var _shadowOffset:FlxPoint = FlxPoint.get(1, 1); + /** * Offset that is applied to the shadow border style, if active. - * x and y are multiplied by borderSize. Default is (1, 1), or lower-right corner. + * `x` and `y` are multiplied by `borderSize`. Default is `(1, 1)`, or lower-right corner. */ - public var shadowOffset(default, null):FlxPoint; + @:deprecated("shadowOffset is deprecated, use setBorderStyle(SHADOW_XY(offsetX, offsetY)), instead") // 5.9.0 + public var shadowOffset(get, never):FlxPoint; /** * Specifies whether the text should have a background. It is recommended to use a @@ -220,8 +226,6 @@ class FlxBitmapText extends FlxSprite this.font = (font == null) ? FlxBitmapFont.getDefaultFont() : font; - shadowOffset = FlxPoint.get(1, 1); - if (FlxG.renderBlit) { pixels = new BitmapData(1, 1, true, FlxColor.TRANSPARENT); @@ -247,7 +251,7 @@ class FlxBitmapText extends FlxSprite _lines = null; _linesWidth = null; - shadowOffset = FlxDestroyUtil.put(shadowOffset); + _shadowOffset = FlxDestroyUtil.put(_shadowOffset); textBitmap = FlxDestroyUtil.dispose(textBitmap); _colorParams = null; @@ -1293,27 +1297,19 @@ class FlxBitmapText extends FlxSprite var delta:Int = Std.int(borderSize / iterations); - var iterationsX:Int = 1; - var iterationsY:Int = 1; - var deltaX:Int = 1; - var deltaY:Int = 1; - - if (borderStyle == FlxTextBorderStyle.SHADOW) - { - iterationsX = Math.round(Math.abs(shadowOffset.x) * borderQuality); - iterationsX = (iterationsX <= 0) ? 1 : iterationsX; - - iterationsY = Math.round(Math.abs(shadowOffset.y) * borderQuality); - iterationsY = (iterationsY <= 0) ? 1 : iterationsY; - - deltaX = Math.round(shadowOffset.x / iterationsX); - deltaY = Math.round(shadowOffset.y / iterationsY); - } - // render border switch (borderStyle) { - case SHADOW: + case SHADOW if (_shadowOffset.x != 1 || _shadowOffset.y != 1): + var iterationsX = Math.round(Math.abs(_shadowOffset.x) * borderQuality); + iterationsX = (iterationsX <= 0) ? 1 : iterationsX; + + var iterationsY = Math.round(Math.abs(_shadowOffset.y) * borderQuality); + iterationsY = (iterationsY <= 0) ? 1 : iterationsY; + + final deltaX = Math.round(_shadowOffset.x / iterationsX); + final deltaY = Math.round(_shadowOffset.y / iterationsY); + for (iterY in 0...iterationsY) { for (iterX in 0...iterationsX) @@ -1321,6 +1317,26 @@ class FlxBitmapText extends FlxSprite drawText(deltaX * (iterX + 1), deltaY * (iterY + 1), isFront, bitmap, useTiles); } } + + case SHADOW: + final iterations = borderQuality < 1 ? 1 : Std.int(Math.abs(borderSize) * borderQuality); + final delta = borderSize / iterations; + var i = iterations + 1; + while (i-- > 1) + { + drawText(Std.int(delta * i), Std.int(delta * i), isFront, bitmap, useTiles); + } + + case SHADOW_XY(shadowX, shadowY): + // Size is max of both, so (4, 4) has 4 iterations, just like SHADOW + final size = Math.max(shadowX, shadowY); + final iterations = borderQuality < 1 ? 1 : Std.int(size * borderQuality); + var i = iterations + 1; + while (i-- > 1) + { + drawText(Std.int(shadowX / iterations * i), Std.int(shadowY / iterations * i), isFront, bitmap, useTiles); + } + case OUTLINE: // Render an outline around the text // (do 8 offset draw calls) @@ -1482,7 +1498,7 @@ class FlxBitmapText extends FlxSprite borderQuality = quality; if (borderStyle == FlxTextBorderStyle.SHADOW) { - shadowOffset.set(borderSize, borderSize); + _shadowOffset.set(borderSize, borderSize); } pendingTextBitmapChange = true; } @@ -1742,7 +1758,12 @@ class FlxBitmapText extends FlxSprite checkPendingChanges(true); return super.get_height(); } - + + inline function get_shadowOffset() + { + return _shadowOffset; + } + /** * Checks if the specified code is one of the Unicode Combining Diacritical Marks * @param Code The charactercode we want to check diff --git a/flixel/text/FlxText.hx b/flixel/text/FlxText.hx index 0c643a152a..3378887100 100644 --- a/flixel/text/FlxText.hx +++ b/flixel/text/FlxText.hx @@ -160,13 +160,24 @@ class FlxText extends FlxSprite public var autoSize(get, set):Bool; var _autoHeight:Bool = true; - + + /** + * Internal handler for deprecated `shadowOffset` field + */ + var _shadowOffset:FlxPoint = FlxPoint.get(1, 1); + /** * Offset that is applied to the shadow border style, if active. * `x` and `y` are multiplied by `borderSize`. Default is `(1, 1)`, or lower-right corner. */ - public var shadowOffset(default, null):FlxPoint; - + @:deprecated("shadowOffset is deprecated, use setBorderStyle(SHADOW_XY(offsetX, offsetY)), instead") // 5.9.0 + public var shadowOffset(get, never):FlxPoint; + + /** + * Used to offset the graphic to account for the border + */ + var _graphicOffset:FlxPoint = FlxPoint.get(0, 0); + var _defaultFormat:TextFormat; var _formatAdjusted:TextFormat; var _formatRanges:Array = []; @@ -239,8 +250,6 @@ class FlxText extends FlxSprite moves = false; drawFrame(); - - shadowOffset = FlxPoint.get(1, 1); } /** @@ -252,7 +261,8 @@ class FlxText extends FlxSprite _font = null; _defaultFormat = null; _formatAdjusted = null; - shadowOffset = FlxDestroyUtil.put(shadowOffset); + _shadowOffset = FlxDestroyUtil.put(_shadowOffset); + _graphicOffset = FlxDestroyUtil.put(_graphicOffset); super.destroy(); } @@ -841,6 +851,11 @@ class FlxText extends FlxSprite regenGraphic(); return super.get_height(); } + + inline function get_shadowOffset() + { + return _shadowOffset; + } override function updateColorTransform():Void { @@ -865,33 +880,47 @@ class FlxText extends FlxSprite { if (textField == null || !_regen) return; - - var oldWidth:Int = 0; - var oldHeight:Int = VERTICAL_GUTTER; - - if (graphic != null) + + final oldWidth:Int = graphic != null ? graphic.width : 0; + final oldHeight:Int = graphic != null ? graphic.height : VERTICAL_GUTTER; + + final newWidthFloat:Float = textField.width; + final newHeightFloat:Float = _autoHeight ? textField.textHeight + VERTICAL_GUTTER : textField.height; + + var borderWidth:Float = 0; + var borderHeight:Float = 0; + switch(borderStyle) { - oldWidth = graphic.width; - oldHeight = graphic.height; + case SHADOW if (_shadowOffset.x != 1 || _shadowOffset.y != 1): + borderWidth += Math.abs(_shadowOffset.x); + borderHeight += Math.abs(_shadowOffset.y); + + case SHADOW: // With the default shadowOffset value + borderWidth += Math.abs(borderSize); + borderHeight += Math.abs(borderSize); + + case SHADOW_XY(offsetX, offsetY): + borderWidth += Math.abs(offsetX); + borderHeight += Math.abs(offsetY); + + case OUTLINE_FAST | OUTLINE: + borderWidth += Math.abs(borderSize) * 2; + borderHeight += Math.abs(borderSize) * 2; + + case NONE: } - - var newWidth:Int = Math.ceil(textField.width); - var textfieldHeight = _autoHeight ? textField.textHeight : textField.height; - var vertGutter = _autoHeight ? VERTICAL_GUTTER : 0; - // Account for gutter - var newHeight:Int = Math.ceil(textfieldHeight) + vertGutter; - + + final newWidth:Int = Math.ceil(newWidthFloat + borderWidth); + final newHeight:Int = Math.ceil(newHeightFloat + borderHeight); + // prevent text height from shrinking on flash if text == "" - if (textField.textHeight == 0) - { - newHeight = oldHeight; - } - - if (oldWidth != newWidth || oldHeight != newHeight) + if (textField.textHeight != 0 && (oldWidth != newWidth || oldHeight != newHeight)) { // Need to generate a new buffer to store the text graphic - var key:String = FlxG.bitmap.getUniqueKey("text"); + final key:String = FlxG.bitmap.getUniqueKey("text"); makeGraphic(newWidth, newHeight, FlxColor.TRANSPARENT, false, key); + width = Math.ceil(newWidthFloat); + height = Math.ceil(newHeightFloat); #if FLX_TRACK_GRAPHICS graphic.trackingInfo = 'text($ID, $text)'; @@ -1002,6 +1031,45 @@ class FlxText extends FlxSprite regenGraphic(); super.draw(); } + + override function drawSimple(camera:FlxCamera):Void + { + // same as super but checks _graphicOffset + getScreenPosition(_point, camera).subtractPoint(offset).subtractPoint(_graphicOffset); + if (isPixelPerfectRender(camera)) + _point.floor(); + + _point.copyToFlash(_flashPoint); + camera.copyPixels(_frame, framePixels, _flashRect, _flashPoint, colorTransform, blend, antialiasing); + } + + override function drawComplex(camera:FlxCamera):Void + { + _frame.prepareMatrix(_matrix, ANGLE_0, checkFlipX(), checkFlipY()); + _matrix.translate(-origin.x, -origin.y); + _matrix.scale(scale.x, scale.y); + + if (bakedRotationAngle <= 0) + { + updateTrig(); + + if (angle != 0) + _matrix.rotateWithTrig(_cosAngle, _sinAngle); + } + + // same as super but checks _graphicOffset + getScreenPosition(_point, camera).subtractPoint(offset).subtractPoint(_graphicOffset); + _point.add(origin.x, origin.y); + _matrix.translate(_point.x, _point.y); + + if (isPixelPerfectRender(camera)) + { + _matrix.tx = Math.floor(_matrix.tx); + _matrix.ty = Math.floor(_matrix.ty); + } + + camera.drawPixels(_frame, framePixels, _matrix, colorTransform, blend, antialiasing, shader); + } /** * Internal function to update the current animation frame. @@ -1019,38 +1087,93 @@ class FlxText extends FlxSprite regenGraphic(); super.calcFrame(RunOnCpp); } - + function applyBorderStyle():Void { - var iterations:Int = Std.int(borderSize * borderQuality); - if (iterations <= 0) + // offset entire image to fit the border + switch(borderStyle) { - iterations = 1; + case SHADOW if (_shadowOffset.x != 1 || _shadowOffset.y != 1): + _graphicOffset.x = _shadowOffset.x > 0 ? _shadowOffset.x : 0; + _graphicOffset.y = _shadowOffset.y > 0 ? _shadowOffset.y : 0; + + case SHADOW: // With the default shadowOffset value + if (borderSize < 0) + _graphicOffset.set(-borderSize, -borderSize); + + case SHADOW_XY(offsetX, offsetY): + _graphicOffset.x = offsetX < 0 ? -offsetX : 0; + _graphicOffset.y = offsetY < 0 ? -offsetY : 0; + + case OUTLINE_FAST | OUTLINE if (borderSize < 0): + _graphicOffset.set(-borderSize, -borderSize); + + case NONE | OUTLINE_FAST | OUTLINE: + _graphicOffset.set(0, 0); } - var delta:Float = borderSize / iterations; - + _matrix.translate(_graphicOffset.x, _graphicOffset.y); + switch (borderStyle) { - case SHADOW: - // Render a shadow beneath the text - // (do one lower-right offset draw call) + case SHADOW if (_shadowOffset.x != 1 || _shadowOffset.y != 1): + // Render a shadow beneath the text using the shadowOffset property applyFormats(_formatAdjusted, true); - + + var iterations = borderQuality < 1 ? 1 : Std.int(Math.abs(borderSize) * borderQuality); + final delta = borderSize / iterations; for (i in 0...iterations) { copyTextWithOffset(delta, delta); } - - _matrix.translate(-shadowOffset.x * borderSize, -shadowOffset.y * borderSize); - + + _matrix.translate(-_shadowOffset.x * borderSize, -_shadowOffset.y * borderSize); + + case SHADOW: // With the default shadowOffset value + // Render a shadow beneath the text + applyFormats(_formatAdjusted, true); + + final originX = _matrix.tx; + final originY = _matrix.ty; + + final iterations = borderQuality < 1 ? 1 : Std.int(Math.abs(borderSize) * borderQuality); + var i = iterations + 1; + while (i-- > 1) + { + copyTextWithOffset(borderSize / iterations * i, borderSize / iterations * i); + // reset to origin + _matrix.tx = originX; + _matrix.ty = originY; + } + + case SHADOW_XY(shadowX, shadowY): + // Render a shadow beneath the text with the specified offset + applyFormats(_formatAdjusted, true); + + final originX = _matrix.tx; + final originY = _matrix.ty; + + // Size is max of both, so (4, 4) has 4 iterations, just like SHADOW + final size = Math.max(shadowX, shadowY); + final iterations = borderQuality < 1 ? 1 : Std.int(size * borderQuality); + var i = iterations + 1; + while (i-- > 1) + { + copyTextWithOffset(shadowX / iterations * i, shadowY / iterations * i); + // reset to origin + _matrix.tx = originX; + _matrix.ty = originY; + } + case OUTLINE: // Render an outline around the text // (do 8 offset draw calls) applyFormats(_formatAdjusted, true); - - var curDelta:Float = delta; - for (i in 0...iterations) + + final iterations = FlxMath.maxInt(1, Std.int(borderSize * borderQuality)); + var i = iterations + 1; + while (i-- > 1) { + final curDelta = borderSize / iterations * i; copyTextWithOffset(-curDelta, -curDelta); // upper-left copyTextWithOffset(curDelta, 0); // upper-middle copyTextWithOffset(curDelta, 0); // upper-right @@ -1059,29 +1182,29 @@ class FlxText extends FlxSprite copyTextWithOffset(-curDelta, 0); // lower-middle copyTextWithOffset(-curDelta, 0); // lower-left copyTextWithOffset(0, -curDelta); // lower-left - + _matrix.translate(curDelta, 0); // return to center - curDelta += delta; } - + case OUTLINE_FAST: // Render an outline around the text // (do 4 diagonal offset draw calls) // (this method might not work with certain narrow fonts) applyFormats(_formatAdjusted, true); - - var curDelta:Float = delta; - for (i in 0...iterations) + + final iterations = FlxMath.maxInt(1, Std.int(borderSize * borderQuality)); + var i = iterations + 1; + while (i-- > 1) { + final curDelta = borderSize / iterations * i; copyTextWithOffset(-curDelta, -curDelta); // upper-left copyTextWithOffset(curDelta * 2, 0); // upper-right copyTextWithOffset(0, curDelta * 2); // lower-right copyTextWithOffset(-curDelta * 2, 0); // lower-left - + _matrix.translate(curDelta, -curDelta); // return to center - curDelta += delta; } - + case NONE: } } @@ -1237,20 +1360,26 @@ class FlxTextFormatMarkerPair enum FlxTextBorderStyle { NONE; - + /** - * A simple shadow to the lower-right. - * Use `FlxText.shadowOffset` for custom placement. + * A simple shadow to the lower-right */ SHADOW; - + + /** + * A shadow that allows custom placement + * **Note:** Ignores borderSize + */ + SHADOW_XY(offsetX:Float, offsetY:Float); + /** * Outline on all 8 sides */ OUTLINE; - + /** - * Outline, optimized using only 4 draw calls (might not work for narrow and/or 1-pixel fonts) + * Outline, optimized using only 4 draw calls + * **Note:** Might not work for narrow and/or 1-pixel fonts */ OUTLINE_FAST; }