diff --git a/flixel/graphics/frames/FlxFrame.hx b/flixel/graphics/frames/FlxFrame.hx index 63008918de..b68ffcf73e 100644 --- a/flixel/graphics/frames/FlxFrame.hx +++ b/flixel/graphics/frames/FlxFrame.hx @@ -139,13 +139,13 @@ class FlxFrame implements IFlxDestroyable */ public var type:FlxFrameType; - var tileMatrix:Vector; - - var blitMatrix:Vector; + /** Internal cache used to draw this frame **/ + var tileMatrix:MatrixVector; + + /** Internal cache used to draw this frame **/ + var blitMatrix:MatrixVector; - @:allow(flixel.graphics.FlxGraphic) - @:allow(flixel.graphics.frames.FlxFramesCollection) - function new(parent:FlxGraphic, angle = FlxFrameAngle.ANGLE_0, flipX = false, flipY = false, duration = 0.0) + public function new(parent:FlxGraphic, angle = FlxFrameAngle.ANGLE_0, flipX = false, flipY = false, duration = 0.0) { this.parent = parent; this.angle = angle; @@ -158,35 +158,21 @@ class FlxFrame implements IFlxDestroyable sourceSize = FlxPoint.get(); offset = FlxPoint.get(); - blitMatrix = new Vector(6); + blitMatrix = new MatrixVector(); if (FlxG.renderTile) - tileMatrix = new Vector(6); + tileMatrix = new MatrixVector(); } @:allow(flixel.graphics.frames.FlxFramesCollection) @:allow(flixel.graphics.frames.FlxBitmapFont) function cacheFrameMatrix():Void { - prepareBlitMatrix(_matrix, true); - blitMatrix[0] = _matrix.a; - blitMatrix[1] = _matrix.b; - blitMatrix[2] = _matrix.c; - blitMatrix[3] = _matrix.d; - blitMatrix[4] = _matrix.tx; - blitMatrix[5] = _matrix.ty; + blitMatrix.copyFrom(this, true); if (FlxG.renderTile) - { - prepareBlitMatrix(_matrix, false); - tileMatrix[0] = _matrix.a; - tileMatrix[1] = _matrix.b; - tileMatrix[2] = _matrix.c; - tileMatrix[3] = _matrix.d; - tileMatrix[4] = _matrix.tx; - tileMatrix[5] = _matrix.ty; - } + tileMatrix.copyFrom(this, false); } - + /** * Applies frame rotation to the specified matrix, which should be used for tiling or blitting. * Required for rotated frame support. @@ -195,7 +181,7 @@ class FlxFrame implements IFlxDestroyable * @param blit Whether specified matrix will be used for blitting or for tile rendering. * @return Transformed matrix. */ - inline function prepareBlitMatrix(mat:FlxMatrix, blit:Bool = true):FlxMatrix + inline function prepareBlitMatrix(mat:FlxMatrix, blit = true):FlxMatrix { mat.identity(); @@ -218,7 +204,7 @@ class FlxFrame implements IFlxDestroyable } /** - * Rotates and flips matrix. This method expects matrix which was prepared by `prepareBlitMatrix()`. + * Rotates and flips matrix. This method expects matrix which was prepared by `MatrixVector.copyTo()`. * Internal use only. * * @param mat Matrix to transform @@ -278,7 +264,7 @@ class FlxFrame implements IFlxDestroyable */ function prepareTransformedBlitMatrix(mat:FlxMatrix, rotation:FlxFrameAngle = FlxFrameAngle.ANGLE_0, flipX:Bool = false, flipY:Bool = false):FlxMatrix { - mat = fillBlitMatrix(mat); + blitMatrix.copyTo(mat); return rotateAndFlip(mat, rotation, flipX, flipY); } @@ -299,12 +285,7 @@ class FlxFrame implements IFlxDestroyable return mat; } - mat.a = tileMatrix[0]; - mat.b = tileMatrix[1]; - mat.c = tileMatrix[2]; - mat.d = tileMatrix[3]; - mat.tx = tileMatrix[4]; - mat.ty = tileMatrix[5]; + tileMatrix.copyTo(mat); var doFlipX = flipX != this.flipX; var doFlipY = flipY != this.flipY; @@ -315,17 +296,6 @@ class FlxFrame implements IFlxDestroyable return rotateAndFlip(mat, rotation, doFlipX, doFlipY); } - inline function fillBlitMatrix(mat:FlxMatrix):FlxMatrix - { - mat.a = blitMatrix[0]; - mat.b = blitMatrix[1]; - mat.c = blitMatrix[2]; - mat.d = blitMatrix[3]; - mat.tx = blitMatrix[4]; - mat.ty = blitMatrix[5]; - return mat; - } - /** * Draws frame on specified `BitmapData` object. * @@ -355,7 +325,7 @@ class FlxFrame implements IFlxDestroyable } else { - fillBlitMatrix(_matrix); + blitMatrix.copyTo(_matrix); if (point != null) _matrix.translate(point.x, point.y); @@ -584,7 +554,53 @@ class FlxFrame implements IFlxDestroyable copyTo(clippedFrame); return clippedFrame.clip(rect); } - + + /** + * Whether there is any overlap between this frame and the given rect. If clipping this frame to + * the given rect would result in an empty frame, the result is `false` + * @since 6.1.0 + */ + public function overlaps(rect:FlxRect) + { + rect.x += frame.x - offset.x; + rect.y += frame.y - offset.y; + final result = rect.overlaps(frame); + rect.x -= frame.x - offset.x; + rect.y -= frame.y - offset.y; + return result; + } + + + /** + * Whether this frame fully contains the given rect. If clipping this frame to + * the given rect would result in a smaller frame, the result is `false` + * @since 6.1.0 + */ + public function contains(rect:FlxRect) + { + rect.x += frame.x - offset.x; + rect.y += frame.y - offset.y; + final result = frame.contains(rect); + rect.x -= frame.x - offset.x; + rect.y -= frame.y - offset.y; + return result; + } + + /** + * Whether this frame is fully contained by the given rect. If clipping this frame to + * the given rect would result in a smaller frame, the result is `false` + * @since 6.1.0 + */ + public function isContained(rect:FlxRect) + { + rect.x += frame.x - offset.x; + rect.y += frame.y - offset.y; + final result = rect.contains(frame); + rect.x -= frame.x - offset.x; + rect.y -= frame.y - offset.y; + return result; + } + /** * Clips this frame to the desired rect * @@ -777,4 +793,125 @@ abstract FlxUVRect(FlxRect) from FlxRect to flixel.util.FlxPool.IFlxPooled { return FlxRect.get(l, t, r, b); } +} + +/** + * Used internally instead of a FlxMatrix, for some unknown reason. + * Perhaps improves performance, tbh, I'm skeptical + */ +abstract MatrixVector(Vector) +{ + public var a(get, set):Float; + inline function get_a() return this[0]; + inline function set_a(value:Float) return this[0] = value; + + public var b(get, set):Float; + inline function get_b() return this[1]; + inline function set_b(value:Float) return this[1] = value; + + public var c(get, set):Float; + inline function get_c() return this[2]; + inline function set_c(value:Float) return this[2] = value; + + public var d(get, set):Float; + inline function get_d() return this[3]; + inline function set_d(value:Float) return this[3] = value; + + public var tx(get, set):Float; + inline function get_tx() return this[4]; + inline function set_tx(value:Float) return this[4] = value; + + public var ty(get, set):Float; + inline function get_ty() return this[5]; + inline function set_ty(value:Float) return this[5] = value; + + + public inline function new () + { + this = new Vector(6); + identity(); + } + + public inline function identity() + { + a = 1; + b = 0; + c = 0; + d = 1; + tx = 0; + ty = 0; + } + + public inline function set(a = 1.0, b = 0.0, c = 0.0, d = 1.0, tx = 0.0, ty = 0.0) + { + set_a(a); + set_b(b); + set_c(c); + set_d(d); + set_tx(tx); + set_ty(ty); + return this; + } + + public inline function translate(dx:Float, dy:Float) + { + tx += dx; + ty += dy; + return this; + } + + public inline function scale(sx:Float, sy:Float) + { + a *= sx; + b *= sy; + c *= sx; + d *= sy; + tx *= sx; + ty *= sy; + return this; + } + + overload public inline extern function copyFrom(frame:FlxFrame, forBlit = true):MatrixVector + { + identity(); + + if (forBlit) + translate(-frame.frame.x, -frame.frame.y); + + if (frame.angle == FlxFrameAngle.ANGLE_90) + { + set(-b, a, -d, c, -ty, tx); + translate(frame.frame.height, 0); + } + else if (frame.angle == FlxFrameAngle.ANGLE_NEG_90) + { + set(b, -a, d, -c, ty, -tx); + translate(0, frame.frame.width); + } + + translate(frame.offset.x, frame.offset.y); + return cast this; + } + + overload public inline extern function copyFrom(mat:FlxMatrix):MatrixVector + { + a = mat.a; + b = mat.b; + c = mat.c; + d = mat.d; + tx = mat.tx; + ty = mat.ty; + return cast this; + } + + public inline function copyTo(mat:FlxMatrix):FlxMatrix + { + mat.a = a; + mat.b = b; + mat.c = c; + mat.d = d; + mat.tx = tx; + mat.ty = ty; + return mat; + } } \ No newline at end of file diff --git a/flixel/math/FlxRect.hx b/flixel/math/FlxRect.hx index cb7715db0e..8c973a377f 100644 --- a/flixel/math/FlxRect.hx +++ b/flixel/math/FlxRect.hx @@ -285,6 +285,23 @@ class FlxRect implements IFlxPooled return result; } + /** + * Checks to see if this rectangle fully contains another + * + * @param rect The other rectangle + * @return Whether this rectangle contains the given rectangle + * @since 6.1.0 + */ + public inline function contains(rect:FlxRect):Bool + { + final result = rect.left >= left + && rect.right <= right + && rect.top >= top + && rect.bottom <= bottom; + rect.putWeak(); + return result; + } + /** * Returns true if this FlxRect contains the FlxPoint * diff --git a/flixel/text/FlxBitmapText.hx b/flixel/text/FlxBitmapText.hx index d2d37e91c0..4d2ed33cfc 100644 --- a/flixel/text/FlxBitmapText.hx +++ b/flixel/text/FlxBitmapText.hx @@ -5,6 +5,8 @@ import flixel.FlxG; import flixel.FlxSprite; import flixel.graphics.frames.FlxBitmapFont; import flixel.graphics.frames.FlxFrame; +import flixel.graphics.tile.FlxDrawBaseItem; +import flixel.math.FlxMatrix; import flixel.math.FlxPoint; import flixel.math.FlxRect; import flixel.text.FlxText.FlxTextAlign; @@ -205,9 +207,9 @@ class FlxBitmapText extends FlxSprite var pendingTextBitmapChange:Bool = true; var pendingPixelsChange:Bool = true; - var textData:Array; - var textDrawData:Array; - var borderDrawData:Array; + var textData:CharList; + var textDrawData:CharList; + var borderDrawData:CharList; /** * Helper bitmap buffer for text pixels but without any color transformations @@ -240,7 +242,6 @@ class FlxBitmapText extends FlxSprite else { textData = []; - textDrawData = []; borderDrawData = []; } @@ -320,7 +321,13 @@ class FlxBitmapText extends FlxSprite } } - override public function draw():Void + // TODO: Make these all local statics when min haxe-ver is 4.3 + static final bgColorTransformDrawHelper = new ColorTransform(); + static final borderColorTransformDrawHelper = new ColorTransform(); + static final textColorTransformDrawHelper = new ColorTransform(); + static final matrixDrawHelper = new FlxMatrix(); + static final frameDrawHelper = new ReusableFrame(); + override function draw() { if (FlxG.renderBlit) { @@ -330,80 +337,35 @@ class FlxBitmapText extends FlxSprite else { checkPendingChanges(true); - - var textLength:Int = Std.int(textDrawData.length / 3); - var borderLength:Int = Std.int(borderDrawData.length / 3); - - var dataPos:Int; - - var cr:Float = color.redFloat; - var cg:Float = color.greenFloat; - var cb:Float = color.blueFloat; - - var borderRed:Float = borderColor.redFloat * cr; - var borderGreen:Float = borderColor.greenFloat * cg; - var borderBlue:Float = borderColor.blueFloat * cb; - var bAlpha:Float = borderColor.alphaFloat * alpha; - - var textRed:Float = cr; - var textGreen:Float = cg; - var textBlue:Float = cb; - var tAlpha:Float = alpha; - + + final colorHelper = Std.int(alpha * 0xFF) << 24 | this.color; + + final textColorTransform = textColorTransformDrawHelper.reset(); + textColorTransform.setMultipliers(colorHelper); if (useTextColor) - { - textRed *= textColor.redFloat; - textGreen *= textColor.greenFloat; - textBlue *= textColor.blueFloat; - tAlpha *= textColor.alphaFloat; - } - - var bgRed:Float = cr; - var bgGreen:Float = cg; - var bgBlue:Float = cb; - var bgAlpha:Float = alpha; - - if (background) - { - bgRed *= backgroundColor.redFloat; - bgGreen *= backgroundColor.greenFloat; - bgBlue *= backgroundColor.blueFloat; - bgAlpha *= backgroundColor.alphaFloat; - } - - var drawItem; - var currFrame:FlxFrame = null; - var currTileX:Float = 0; - var currTileY:Float = 0; - var sx:Float = scale.x * _facingHorizontalMult; - var sy:Float = scale.y * _facingVerticalMult; - - var ox:Float = origin.x; - var oy:Float = origin.y; - - if (_facingHorizontalMult != 1) - { - ox = frameWidth - ox; - } - if (_facingVerticalMult != 1) - { - oy = frameHeight - oy; - } - - var clippedFrameRect; + textColorTransform.scaleMultipliers(textColor); + + final borderColorTransform = borderColorTransformDrawHelper.reset(); + borderColorTransform.setMultipliers(borderColor).scaleMultipliers(colorHelper); + + final scaleX:Float = scale.x * _facingHorizontalMult; + final scaleY:Float = scale.y * _facingVerticalMult; + + final originX:Float = _facingHorizontalMult != 1 ? frameWidth - origin.x : origin.x; + final originY:Float = _facingVerticalMult != 1 ? frameHeight - origin.y : origin.y; + + final clippedFrameRect = FlxRect.get(0, 0, frameWidth, frameHeight); if (clipRect != null) - { - clippedFrameRect = clipRect.intersection(FlxRect.weak(0, 0, frameWidth, frameHeight)); - - if (clippedFrameRect.isEmpty) - return; - } - else - { - clippedFrameRect = FlxRect.get(0, 0, frameWidth, frameHeight); - } + clippedFrameRect.clipTo(clipRect); + if (clippedFrameRect.isEmpty) + return; + + final charClipHelper = FlxRect.get(); + final charClippedFrame = frameDrawHelper; + final screenPos = FlxPoint.get(); + final cameras = getCamerasLegacy(); for (camera in cameras) { @@ -412,11 +374,11 @@ class FlxBitmapText extends FlxSprite continue; } - getScreenPosition(_point, camera).subtractPoint(offset); + getScreenPosition(screenPos, camera).subtractPoint(offset); if (isPixelPerfectRender(camera)) { - _point.floor(); + screenPos.floor(); } updateTrig(); @@ -424,90 +386,60 @@ class FlxBitmapText extends FlxSprite if (background) { // backround tile transformations - currFrame = FlxG.bitmap.whitePixel; - _matrix.identity(); - _matrix.scale(0.1 * clippedFrameRect.width, 0.1 * clippedFrameRect.height); - _matrix.translate(clippedFrameRect.x - ox, clippedFrameRect.y - oy); - _matrix.scale(sx, sy); + final matrix = matrixDrawHelper; + matrix.identity(); + matrix.scale(0.1 * clippedFrameRect.width, 0.1 * clippedFrameRect.height); + matrix.translate(clippedFrameRect.x - originX, clippedFrameRect.y - originY); + matrix.scale(scaleX, scaleY); if (angle != 0) { - _matrix.rotateWithTrig(_cosAngle, _sinAngle); + matrix.rotateWithTrig(_cosAngle, _sinAngle); } - _matrix.translate(_point.x + ox, _point.y + oy); - _colorParams.setMultipliers(bgRed, bgGreen, bgBlue, bgAlpha); - camera.drawPixels(currFrame, null, _matrix, _colorParams, blend, antialiasing); + matrix.translate(screenPos.x + originX, screenPos.y + originY); + final colorTransform = bgColorTransformDrawHelper.reset(); + colorTransform.setMultipliers(colorHelper).scaleMultipliers(backgroundColor); + camera.drawPixels(FlxG.bitmap.whitePixel, null, matrix, colorTransform, blend, antialiasing); } - - var hasColorOffsets:Bool = (colorTransform != null && colorTransform.hasRGBAOffsets()); - - drawItem = camera.startQuadBatch(font.parent, true, hasColorOffsets, blend, antialiasing, shader); - - for (j in 0...borderLength) - { - dataPos = j * 3; - - currFrame = font.getCharFrame(Std.int(borderDrawData[dataPos])); - - currTileX = borderDrawData[dataPos + 1]; - currTileY = borderDrawData[dataPos + 2]; - - if (clipRect != null) - { - clippedFrameRect.copyFrom(clipRect).offset(-currTileX, -currTileY); - currFrame = currFrame.clipTo(clippedFrameRect); - } - - currFrame.prepareMatrix(_matrix); - _matrix.translate(currTileX - ox, currTileY - oy); - _matrix.scale(sx, sy); - if (angle != 0) - { - _matrix.rotateWithTrig(_cosAngle, _sinAngle); - } - - _matrix.translate(_point.x + ox, _point.y + oy); - _colorParams.setMultipliers(borderRed, borderGreen, borderBlue, bAlpha); - drawItem.addQuad(currFrame, _matrix, _colorParams); - } - - for (j in 0...textLength) + + final hasColorOffsets = (colorTransform != null && colorTransform.hasRGBAOffsets()); + final drawItem = camera.startQuadBatch(font.parent, true, hasColorOffsets, blend, antialiasing, shader); + function addQuad(charCode:Int, x:Float, y:Float, color:ColorTransform) { - dataPos = j * 3; - - currFrame = font.getCharFrame(Std.int(textDrawData[dataPos])); - - currTileX = textDrawData[dataPos + 1]; - currTileY = textDrawData[dataPos + 2]; - + var frame = font.getCharFrame(charCode); if (clipRect != null) { - clippedFrameRect.copyFrom(clipRect).offset(-currTileX, -currTileY); - currFrame = currFrame.clipTo(clippedFrameRect); + charClipHelper.copyFrom(clippedFrameRect).offset(-x, -y); + if (!frame.isContained(charClipHelper)) + frame = frame.clipTo(charClipHelper, charClippedFrame); } - - currFrame.prepareMatrix(_matrix); - _matrix.translate(currTileX - ox, currTileY - oy); - _matrix.scale(sx, sy); + + final matrix = matrixDrawHelper; + frame.prepareMatrix(matrix); + matrix.translate(x - originX, y - originY); + matrix.scale(scaleX, scaleY); if (angle != 0) { - _matrix.rotateWithTrig(_cosAngle, _sinAngle); + matrix.rotateWithTrig(_cosAngle, _sinAngle); } - - _matrix.translate(_point.x + ox, _point.y + oy); - _colorParams.setMultipliers(textRed, textGreen, textBlue, tAlpha); - drawItem.addQuad(currFrame, _matrix, _colorParams); + + matrix.translate(screenPos.x + originX, screenPos.y + originY); + drawItem.addQuad(frame, matrix, color); } - + + borderDrawData.forEach(addQuad.bind(_, _, _, borderColorTransform)); + textDrawData.forEach(addQuad.bind(_, _, _, textColorTransform)); #if FLX_DEBUG FlxBasic.visibleCount++; #end } - - // dispose clipRect helpers + + // dispose helpers + charClipHelper.put(); clippedFrameRect.put(); - + screenPos.put(); + #if FLX_DEBUG if (FlxG.debugger.drawDebug) { @@ -516,7 +448,7 @@ class FlxBitmapText extends FlxSprite #end } } - + override function set_clipRect(Rect:FlxRect):FlxRect { super.set_clipRect(Rect); @@ -1091,7 +1023,7 @@ class FlxBitmapText extends FlxSprite } else if (FlxG.renderTile) { - textData.splice(0, textData.length); + textData.clear(); } _fieldWidth = frameWidth; @@ -1154,19 +1086,15 @@ class FlxBitmapText extends FlxSprite function blitLine(line:UnicodeString, startX:Int, startY:Int):Void { - var data:Array = []; + final data:CharList = []; addLineData(line, startX, startY, data); - while (data.length > 0) + data.forEach(function (charCode, x, y) { - final charCode = Std.int(data.shift()); - final x = data.shift(); - final y = data.shift(); - final charFrame = font.getCharFrame(charCode); _flashPoint.setTo(x, y); charFrame.paint(textBitmap, _flashPoint, true); - } + }); } function tileLine(line:UnicodeString, startX:Int, startY:Int) @@ -1177,10 +1105,8 @@ class FlxBitmapText extends FlxSprite addLineData(line, startX, startY, textData); } - function addLineData(line:UnicodeString, startX:Int, startY:Int, data:Array) + function addLineData(line:UnicodeString, startX:Int, startY:Int, data:CharList) { - var pos:Int = data.length; - var curX:Float = startX; var curY:Int = startY; @@ -1204,11 +1130,7 @@ class FlxBitmapText extends FlxSprite final isSpace = isSpaceChar(charCode); final hasFrame = font.charExists(charCode); if (hasFrame && !isSpace) - { - data[pos++] = charCode; - data[pos++] = curX; - data[pos++] = curY; - } + data.push(charCode, curX, curY); if (hasFrame || isSpace) { @@ -1281,8 +1203,8 @@ class FlxBitmapText extends FlxSprite } else { - textDrawData.splice(0, textDrawData.length); - borderDrawData.splice(0, borderDrawData.length); + textDrawData.clear(); + borderDrawData.clear(); } if (autoBounds) @@ -1294,14 +1216,25 @@ class FlxBitmapText extends FlxSprite bitmap.lock(); } - var isFront:Bool = false; + forEachBorder(drawText.bind(_, _, false, bitmap, useTiles)); + drawText(0, 0, true, bitmap, useTiles); - var iterations:Int = Std.int(borderSize * borderQuality); - iterations = (iterations <= 0) ? 1 : iterations; + if (!useTiles) + { + bitmap.unlock(); + } - var delta:Int = Std.int(borderSize / iterations); + if (FlxG.renderBlit) + { + dirty = true; + } - // render border + if (pendingPixelsChange) + throw "pendingPixelsChange was changed to true while processing changed pixels"; + } + + function forEachBorder(func:(xOffset:Int, yOffset:Int)->Void) + { switch (borderStyle) { case SHADOW if (_shadowOffset.x != 1 || _shadowOffset.y != 1): @@ -1318,7 +1251,7 @@ class FlxBitmapText extends FlxSprite { for (iterX in 0...iterationsX) { - drawText(deltaX * (iterX + 1), deltaY * (iterY + 1), isFront, bitmap, useTiles); + func(deltaX * (iterX + 1), deltaY * (iterY + 1)); } } @@ -1328,7 +1261,7 @@ class FlxBitmapText extends FlxSprite var i = iterations + 1; while (i-- > 1) { - drawText(Std.int(delta * i), Std.int(delta * i), isFront, bitmap, useTiles); + func(Std.int(delta * i), Std.int(delta * i)); } case SHADOW_XY(shadowX, shadowY): @@ -1338,68 +1271,41 @@ class FlxBitmapText extends FlxSprite var i = iterations + 1; while (i-- > 1) { - drawText(Std.int(shadowX / iterations * i), Std.int(shadowY / iterations * i), isFront, bitmap, useTiles); + func(Std.int(shadowX / iterations * i), Std.int(shadowY / iterations * i)); } case OUTLINE: - // Render an outline around the text - // (do 8 offset draw calls) - var itd:Int = 0; + // Render an outline around the text (8 draws) + var iterations:Int = Std.int(borderSize * borderQuality); + iterations = (iterations <= 0) ? 1 : iterations; + final delta = Std.int(borderSize / iterations); for (iter in 0...iterations) { - itd = delta * (iter + 1); - // upper-left - drawText(-itd, -itd, isFront, bitmap, useTiles); - // upper-middle - drawText(0, -itd, isFront, bitmap, useTiles); - // upper-right - drawText(itd, -itd, isFront, bitmap, useTiles); - // middle-left - drawText(-itd, 0, isFront, bitmap, useTiles); - // middle-right - drawText(itd, 0, isFront, bitmap, useTiles); - // lower-left - drawText(-itd, itd, isFront, bitmap, useTiles); - // lower-middle - drawText(0, itd, isFront, bitmap, useTiles); - // lower-right - drawText(itd, itd, isFront, bitmap, useTiles); + final i = delta * (iter + 1); + func(-i, -i); // upper-left + func( 0, -i); // upper-middle + func( i, -i); // upper-right + func(-i, 0); // middle-left + func( i, 0); // middle-right + func(-i, i); // lower-left + func( 0, i); // lower-middle + func( i, i); // lower-right } case OUTLINE_FAST: - // Render an outline around the text - // (do 4 diagonal offset draw calls) - // (this method might not work with certain narrow fonts) - var itd:Int = 0; + // Render an outline around the text in each corner (4 draws) + var iterations:Int = Std.int(borderSize * borderQuality); + iterations = (iterations <= 0) ? 1 : iterations; + final delta = Std.int(borderSize / iterations); for (iter in 0...iterations) { - itd = delta * (iter + 1); - // upper-left - drawText(-itd, -itd, isFront, bitmap, useTiles); - // upper-right - drawText(itd, -itd, isFront, bitmap, useTiles); - // lower-left - drawText(-itd, itd, isFront, bitmap, useTiles); - // lower-right - drawText(itd, itd, isFront, bitmap, useTiles); + final i = delta * (iter + 1); + func(-i, -i); // upper-left + func( i, -i); // upper-right + func(-i, i); // lower-left + func( i, i); // lower-right } case NONE: } - - isFront = true; - drawText(0, 0, isFront, bitmap, useTiles); - - if (!useTiles) - { - bitmap.unlock(); - } - - if (FlxG.renderBlit) - { - dirty = true; - } - - if (pendingPixelsChange) - throw "pendingPixelsChange was changed to true while processing changed pixels"; } function autoAdjustBounds() @@ -1410,7 +1316,7 @@ class FlxBitmapText extends FlxSprite offset.set(-0.5 * (newWidth - frameWidth), -0.5 * (newHeight - frameHeight)); centerOrigin(); } - + function drawText(posX:Int, posY:Int, isFront:Bool = true, ?bitmap:BitmapData, useTiles:Bool = false):Void { if (FlxG.renderBlit) @@ -1428,11 +1334,10 @@ class FlxBitmapText extends FlxSprite } } + // TODO: Make this a local statics when min haxe-ver is 4.3 + static final matrixBlitHelper = new FlxMatrix(); function blitText(posX:Int, posY:Int, isFront:Bool = true, ?bitmap:BitmapData):Void { - _matrix.identity(); - _matrix.translate(posX, posY); - var colorToApply = FlxColor.WHITE; if (isFront && useTextColor) @@ -1453,42 +1358,34 @@ class FlxBitmapText extends FlxSprite } else { - bitmap.draw(textBitmap, _matrix, _colorParams); + matrixBlitHelper.identity(); + matrixBlitHelper.translate(posX, posY); + bitmap.draw(textBitmap, matrixBlitHelper, _colorParams); } } - + function tileText(posX:Int, posY:Int, isFront:Bool = true):Void { if (!FlxG.renderTile) return; - - var data:Array = isFront ? textDrawData : borderDrawData; - - var pos:Int = data.length; - var textPos:Int; - var textLen:Int = Std.int(textData.length / 3); - var rect = FlxRect.get(); - var frameVisible; - - for (i in 0...textLen) + + final data:CharList = isFront ? textDrawData : borderDrawData; + final rect = FlxRect.get(); + + textData.forEach(function (charCode:Int, charX:Float, charY:Float) { - textPos = 3 * i; - - frameVisible = true; - + final charFrame = font.getCharFrame(charCode); + if (clipRect != null) { - rect.copyFrom(clipRect).offset(-textData[textPos + 1] - posX, -textData[textPos + 2] - posY); - frameVisible = font.getCharFrame(Std.int(textData[textPos])).clipTo(rect).type != FlxFrameType.EMPTY; - } - - if (frameVisible) - { - data[pos++] = textData[textPos]; - data[pos++] = textData[textPos + 1] + posX; - data[pos++] = textData[textPos + 2] + posY; + rect.copyFrom(clipRect); + rect.offset(-charX - posX, -charY - posY); + if (!charFrame.overlaps(rect)) + return; } - } + + data.push(charCode, charX + posX, charY + posY); + }); rect.put(); } @@ -1841,6 +1738,68 @@ enum WordSplitConditions WIDTH(minPixels:Int); } +@:forward(length) +abstract CharList(Array) from Array +{ + public inline function new () + { + this = []; + } + + // TODO: deprecate + overload public inline extern function push(item:Float) + { + this.push(item); + } + + overload public inline extern function push(charCode:Int, x:Float, y:Float) + { + this.push(charCode); + this.push(x); + this.push(y); + } + + public function forEach(func:(charCode:Int, x:Float, y:Float)->Void) + { + for (i in 0...Std.int(this.length / 3)) + { + final pos = i * 3; + func(Std.int(this[pos]), this[pos + 1], this[pos + 2]); + } + } + + public inline function clear() + { + this.resize(0); + } + + @:arrayAccess // TODO: deprecate + public inline function get(index:Int):Float + { + return this[index]; + } + @:arrayAccess // TODO: deprecate + public inline function set(index:Int, value:Float):Float + { + return this[index] = value; + } +} + +/** + * Helper to avoid creating a new frame every draw call + */ +private class ReusableFrame extends FlxFrame +{ + public function new () + { + super(null); + // We need to define this now, since it's created before renderTile is set + tileMatrix = new MatrixVector(); + } + + override function destroy() {} +} + /* * TODO - enum WordSplitMethod: determines how words look when split, ex: * * whether split words start on a new line diff --git a/flixel/util/FlxColor.hx b/flixel/util/FlxColor.hx index 8c6328b84e..e3fe2eb9fd 100644 --- a/flixel/util/FlxColor.hx +++ b/flixel/util/FlxColor.hx @@ -517,7 +517,7 @@ abstract FlxColor(Int) from Int from UInt to Int to UInt * @param Alpha How opaque the color should be, either between 0 and 1 or 0 and 255. * @return This color */ - public inline function setHSB(Hue:Float, Saturation:Float, Brightness:Float, Alpha:Float):FlxColor + public inline function setHSB(Hue:Float, Saturation:Float, Brightness:Float, Alpha = 1.0):FlxColor { var chroma = Brightness * Saturation; var match = Brightness - chroma; @@ -533,7 +533,7 @@ abstract FlxColor(Int) from Int from UInt to Int to UInt * @param Alpha How opaque the color should be, either between 0 and 1 or 0 and 255 * @return This color */ - public inline function setHSL(Hue:Float, Saturation:Float, Lightness:Float, Alpha:Float):FlxColor + public inline function setHSL(Hue:Float, Saturation:Float, Lightness:Float, Alpha = 1.0):FlxColor { var chroma = (1 - Math.abs(2 * Lightness - 1)) * Saturation; var match = Lightness - chroma / 2; diff --git a/flixel/util/FlxColorTransformUtil.hx b/flixel/util/FlxColorTransformUtil.hx index c13f7b4e01..9652bbd972 100644 --- a/flixel/util/FlxColorTransformUtil.hx +++ b/flixel/util/FlxColorTransformUtil.hx @@ -4,26 +4,174 @@ import openfl.geom.ColorTransform; class FlxColorTransformUtil { - public static function setMultipliers(transform:ColorTransform, red:Float, green:Float, blue:Float, alpha:Float):ColorTransform + /** + * Resets the transform to default values, multipliers become `1.0` and offsets become `0.0` + * @since 6.1.0 + */ + public static inline function reset(transform:ColorTransform):ColorTransform + { + return set(transform, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0); + } + + /** + * Quick way to set all of a transform's values + * + * @param rMult The value for the red multiplier, ranges from 0 to 1 + * @param gMult The value for the green multiplier, ranges from 0 to 1 + * @param bMult The value for the blue multiplier, ranges from 0 to 1 + * @param aMult The value for the alpha transparency multiplier, ranges from 0 to 1 + * @param rOffset The offset value for the red color channel, ranges from -255 to 255 + * @param gOffset The offset value for the green color channel, ranges from -255 to 255 + * @param bOffset The offset for the blue color channel value, ranges from -255 to 255 + * @param aOffset The offset for alpha transparency channel value, ranges from -255 to 255 + * @since 6.1.0 + */ + overload public static inline extern function set(transform:ColorTransform, + rMult, gMult, bMult, aMult, + rOffset, gOffset, bOffset, aOffset = 0.0):ColorTransform + { + setMultipliers(transform, rMult, gMult, bMult, aMult); + setOffsets(transform, rOffset, gOffset, bOffset, aOffset); + + return transform; + } + + /** + * Quick way to set all of a transform's values + * + * @param rMult The value for the red multiplier, ranges from 0 to 1 + * @param gMult The value for the green multiplier, ranges from 0 to 1 + * @param bMult The value for the blue multiplier, ranges from 0 to 1 + * @param rOffset The offset value for the red color channel, ranges from -255 to 255 + * @param gOffset The offset value for the green color channel, ranges from -255 to 255 + * @param bOffset The offset for the blue color channel value, ranges from -255 to 255 + * @since 6.1.0 + */ + overload public static inline extern function set(transform:ColorTransform, + rMult, gMult, bMult, + rOffset, gOffset, bOffset):ColorTransform + { + return set(transform, rMult, gMult, bMult, 1.0, rOffset, gOffset, bOffset); + } + + /** + * Quick way to set all of a transform's values + * + * @param colorMult A `FlxColor` whos `redFloat`, `greenFloat`, `blueFloat` and + * `alphaFloat` values determine the multipliers of this transform + * @param color A `FlxColor` whos `red`, `green`, `blue` and `alpha` values + * determine the offsets of this transform + * @since 6.1.0 + */ + overload public static inline extern function set(transform:ColorTransform, colorMult = FlxColor.WHITE, colorOffset:FlxColor = 0x0):ColorTransform + { + return set(transform, + colorMult.redFloat, colorMult.greenFloat, colorMult.blueFloat, colorMult.alphaFloat, + colorOffset.red, colorOffset.green, colorOffset.blue, colorOffset.alpha + ); + } + + /** + * Scales each color's multiplier by the specifified amount + * + * @param rMult The amount to scale the red multiplier + * @param gMult The amount to scale the green multiplier + * @param bMult The amount to scale the blue multiplier + * @param aMult The amount to scale the alpha transparency multiplier + * @return ColorTransform + * @since 6.1.0 + */ + overload public static inline extern function scaleMultipliers(transform:ColorTransform, rMult:Float, gMult:Float, bMult:Float, aMult = 1.0):ColorTransform + { + transform.redMultiplier *= rMult; + transform.greenMultiplier *= gMult; + transform.blueMultiplier *= bMult; + transform.alphaMultiplier *= aMult; + + return transform; + } + + /** + * Scales each color's multiplier by the color's specifified normal values + * + * @param color A `FlxColor` whos `redFloat`, `greenFloat`, `blueFloat` and + * `alphaFloat` values scale the multipliers of this transform + * @since 6.1.0 + */ + overload public static inline extern function scaleMultipliers(transform:ColorTransform, color:FlxColor):ColorTransform + { + transform.redMultiplier *= color.redFloat; + transform.greenMultiplier *= color.greenFloat; + transform.blueMultiplier *= color.blueFloat; + transform.alphaMultiplier *= color.alphaFloat; + + return transform; + } + + /** + * Quick way to set all of a transform's multipliers + * + * @param rMult The value for the red multiplier, ranges from 0 to 1 + * @param gMult The value for the green multiplier, ranges from 0 to 1 + * @param bMult The value for the blue multiplier, ranges from 0 to 1 + * @param aMult The value for the alpha transparency multiplier, ranges from 0 to 1 + */ + overload public static inline extern function setMultipliers(transform:ColorTransform, red:Float, green:Float, blue:Float, alpha = 1.0):ColorTransform { transform.redMultiplier = red; transform.greenMultiplier = green; transform.blueMultiplier = blue; transform.alphaMultiplier = alpha; - + return transform; } - - public static function setOffsets(transform:ColorTransform, red:Float, green:Float, blue:Float, alpha:Float):ColorTransform + + /** + * Quick way to set all of a transform's multipliers with a single color + * + * @param color A `FlxColor` whos `redFloat`, `greenFloat`, `blueFloat` and + * `alphaFloat` values determine the multipliers of this transform + * @since 6.1.0 + */ + overload public static inline extern function setMultipliers(transform:ColorTransform, color:FlxColor):ColorTransform + { + transform.redMultiplier = color.redFloat; + transform.greenMultiplier = color.greenFloat; + transform.blueMultiplier = color.blueFloat; + transform.alphaMultiplier = color.alphaFloat; + + return transform; + } + + /** + * Quick way to set all of a transform's offsets + * + * @param red The value for the red offset, ranges from 0 to 255 + * @param green The value for the green offset, ranges from 0 to 255 + * @param blue The value for the blue offset, ranges from 0 to 255 + * @param alpha The value for the alpha transparency offset, ranges from 0 to 255 + */ + overload public static inline extern function setOffsets(transform:ColorTransform, red:Float, green:Float, blue:Float, alpha = 0.0):ColorTransform { transform.redOffset = red; transform.greenOffset = green; transform.blueOffset = blue; transform.alphaOffset = alpha; - + return transform; } - + + /** + * Quick way to set all of a transform's offsets with a single color + * + * @param color A `FlxColor` whos `red`, `green`, `blue` and + * `alpha` values determine the offsets of this transform + * @since 6.1.0 + */ + overload public static inline extern function setOffsets(transform:ColorTransform, color:FlxColor):ColorTransform + { + return setOffsets(transform, color.red, color.green, color.blue, color.alpha); + } /** * Returns whether red, green, or blue multipliers are set to anything other than 1. */ diff --git a/tests/unit/src/flixel/graphics/frames/FlxFrameTest.hx b/tests/unit/src/flixel/graphics/frames/FlxFrameTest.hx index 8ad8991c6e..1a3bdf8d4d 100644 --- a/tests/unit/src/flixel/graphics/frames/FlxFrameTest.hx +++ b/tests/unit/src/flixel/graphics/frames/FlxFrameTest.hx @@ -1,6 +1,10 @@ package flixel.graphics.frames; +import flixel.FlxSprite; +import flixel.math.FlxRect; +import haxe.PosInfos; import massive.munit.Assert; +import openfl.display.BitmapData; @:access(flixel.graphics.frames.FlxFrame.new) class FlxFrameTest extends FlxTest @@ -36,6 +40,81 @@ class FlxFrameTest extends FlxTest Assert.areEqual("split/" + i, frames[i].name); } + @Test + function testOverlaps() + { + final frames = createFrames("overlaps", 100, 100, 10, 10, 0); + final rect = FlxRect.get(); + + inline function assertOverlaps(x = 0, y = 0, width = 50, height = 50, ?pos:PosInfos) + { + for (frame in frames) + Assert.isTrue(frame.overlaps(rect.set(x, y, width, height)), + 'expected overlap - rect: $rect frame: { offset: ${frame.offset}, rect: ${frame.frame} }', pos); + } + + inline function assertNotOverlaps(x = 0, y = 0, width = 50, height = 50, ?pos:PosInfos) + { + for (frame in frames) + Assert.isFalse(frame.overlaps(rect.set(x, y, width, height)), + 'expected NO overlap - rect: $rect frame: { offset: ${frame.offset}, rect: ${frame.frame} }', pos); + } + + assertOverlaps(25, 25); + assertNotOverlaps(-50, -50); + assertOverlaps(-49, -49); + assertNotOverlaps(100, 100); + assertOverlaps(99, 99); + Assert.isTrue(true); + } + + @Test + @Ignore// TODO: figure out exactly what offset is for + function testOverlapsOffset() + { + final frames = createFrames("overlaps", 110, 110, 10, 10, 5); + final rect = FlxRect.get(); + + inline function assertOverlaps(x = 0, y = 0, width = 50, height = 50, ?pos:PosInfos) + { + for (frame in frames) + Assert.isTrue(frame.overlaps(rect.set(x, y, width, height)), + 'expected overlap - rect: $rect frame: { offset: ${frame.offset}, rect: ${frame.frame} }', pos); + } + + inline function assertNotOverlaps(x = 0, y = 0, width = 50, height = 50, ?pos:PosInfos) + { + for (frame in frames) + Assert.isFalse(frame.overlaps(rect.set(x, y, width, height)), + 'expected NO overlap - rect: $rect frame: { offset: ${frame.offset}, rect: ${frame.frame} }', pos); + } + + assertOverlaps(25, 25); + assertNotOverlaps(-50, -50); + assertOverlaps(-49, -49); + assertNotOverlaps(100, 100); + assertOverlaps(99, 99); + Assert.isTrue(true); + } + + function createFrames(name:String, width = 100, height = 100, cols = 10, rows = 10, buffer = 0):Array + { + final sprite = new FlxSprite(0, 0); + sprite.loadGraphic(new BitmapData(width * cols, height * rows), true, width, height, true, name); + if (buffer > 0) + { + for (frame in sprite.frames.frames) + { + frame.offset.set(buffer, buffer); + frame.frame.x += buffer; + frame.frame.y += buffer; + frame.frame.width -= buffer * 2; + frame.frame.height -= buffer * 2; + } + } + + return sprite.frames.frames; + } function createFrame(name:String):FlxFrame { var frame = new FlxFrame(null); diff --git a/tests/unit/src/flixel/math/FlxRectTest.hx b/tests/unit/src/flixel/math/FlxRectTest.hx index 2f501a65de..0a7c24d6ac 100644 --- a/tests/unit/src/flixel/math/FlxRectTest.hx +++ b/tests/unit/src/flixel/math/FlxRectTest.hx @@ -1,7 +1,7 @@ package flixel.math; -import massive.munit.Assert; import haxe.PosInfos; +import massive.munit.Assert; class FlxRectTest extends FlxTest { @@ -150,4 +150,28 @@ class FlxRectTest extends FlxTest expected.put(); } + + @Test + function testContins() + { + rect1.set(0, 0, 100, 100); + + inline function assertContains(x, y, width = 50, height = 50, ?pos:PosInfos) + { + Assert.isTrue(rect1.contains(rect2.set(x, y, width, height)), pos); + } + + inline function assertNotContains(x, y, width = 50, height = 50, ?pos:PosInfos) + { + Assert.isFalse(rect1.contains(rect2.set(x, y, width, height)), pos); + } + + assertContains(25, 25); + assertContains(0, 0); + assertNotContains(-1, -1); + assertContains(50, 50); + assertNotContains(51, 51); + assertContains(0, 0, 100, 100); + assertNotContains(-1, -1, 101, 101); + } } diff --git a/tests/unit/src/flixel/system/frontEnds/ConsoleFrontEndTest.hx b/tests/unit/src/flixel/system/frontEnds/ConsoleFrontEndTest.hx index 122e85d210..6f783afc1f 100644 --- a/tests/unit/src/flixel/system/frontEnds/ConsoleFrontEndTest.hx +++ b/tests/unit/src/flixel/system/frontEnds/ConsoleFrontEndTest.hx @@ -1,10 +1,10 @@ package flixel.system.frontEnds; -import haxe.Exception; -import haxe.PosInfos; import flixel.FlxG; import flixel.math.FlxPoint; import flixel.system.debug.console.ConsoleUtil; +import haxe.Exception; +import haxe.PosInfos; import massive.munit.Assert; class ConsoleFrontEndTest @@ -36,12 +36,13 @@ class ConsoleFrontEndTest var func = ()->"success"; FlxG.console.registerFunction("func", func); assertCommandSucceedsWith("func()", "success"); + // assertCommandSucceedsWith("func", "success");// TODO: no (), should work, doesn't FlxG.console.removeByAlias("func"); assertCommandFails("func()"); //test again with removeFunction FlxG.console.registerFunction("func", func); - assertCommandSucceedsWith("func", "success");// omit (), should still work + assertCommandSucceedsWith("func()", "success"); FlxG.console.removeFunction(func); assertCommandFails("func()"); } @@ -59,7 +60,11 @@ class ConsoleFrontEndTest //test again with removeFunction FlxG.console.registerObject("p", p); assertCommandSucceedsWith("p.set(0, 0)", p); - FlxG.console.removeObject("p"); + FlxG.console.removeByAlias("p"); + assertCommandFails("p.set(50, 100)"); + FlxG.console.registerObject("p", p); + assertCommandSucceedsWith("p.set(0, 0)", p); + FlxG.console.removeObject(p); assertCommandFails("p.set(50, 100)"); } diff --git a/tests/unit/src/flixel/util/FlxColorTransformUtilTest.hx b/tests/unit/src/flixel/util/FlxColorTransformUtilTest.hx new file mode 100644 index 0000000000..cea7046d91 --- /dev/null +++ b/tests/unit/src/flixel/util/FlxColorTransformUtilTest.hx @@ -0,0 +1,225 @@ +package flixel.util; + +import haxe.PosInfos; +import massive.munit.Assert; +import openfl.geom.ColorTransform; + +using flixel.util.FlxColorTransformUtil; + +class FlxColorTransformUtilTest extends FlxTest +{ + var color:ColorTransform; + + @Before + function before():Void + { + color = new ColorTransform(); + } + + @Test + function testReset() + { + color.set(0, 0.5, 1.0, 1.5, -0.5, 0, 0.5, 1.0); + color.reset().reset(); + assertCTEquals(1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0); + } + + @Test + function testSetRGBA() + { + color.set(0.0, 0.5, 1.0, 1.5, -0.5, 0.0, 0.5, 1.0); + assertCTEquals(0, 0.5, 1.0, 1.5, -0.5, 0, 0.5, 1.0); + + //test optional alpha args + #if !flash + color.set(0, 0.25, 0.5, -0.5, 0, 0.5); + assertCTEquals(0, 0.25, 0.5, 1.0, -0.5, 0, 0.5, 0); + #else + color.set(0, 0.25, 0.5, 1.0, -0.5, 0, 0.5); + assertCTEquals(0, 0.25, 0.5, 1.0, -0.5, 0, 0.5, 0); + #end + } + + @Test + function testSetColor() + { + color.set(0xFFBB7733, 0xCC884400); + assertCTEquals(0xBB/0xFF, 0x77/0xFF, 0x33/0xFF, 1.0, 0x88, 0x44, 0.0, 0xCC); + color.set(0xEEAA6622); + assertCTEquals(0xAA/0xFF, 0x66/0xFF, 0x22/0xFF, 0xEE/0xFF, 0, 0, 0, 0); + final c:FlxColor = 0xDD995511; + // test chaining + color.set(0x0).set(c); + assertCTEquals(c.redFloat, c.greenFloat, c.blueFloat, c.alphaFloat, 0, 0, 0, 0); + } + + @Test + function testScaleMultipliersRGBA() + { + color.scaleMultipliers(0.4, 0.5, 0.6, 1.5); + assertCTEquals(0.4, 0.5, 0.6, 1.5, 0, 0, 0, 0); + color.scaleMultipliers(0.4, 0.5, 0.6); + assertCTEquals(0.16, 0.25, 0.36, 1.5, 0, 0, 0, 0); + } + + @Test + function testScaleMultipliersColor() + { + color.scaleMultipliers(0x115599dd); + assertCTEquals(0x55/0xFF, 0x99/0xFF, 0xDD/0xFF, 0x11/0xFF, 0, 0, 0, 0); + final c:FlxColor = 0x115599dd; + color.reset(); + // test chaining + color.scaleMultipliers(c).scaleMultipliers(c); + inline function sqr(n:Float) return n*n; + assertCTEquals(sqr(c.redFloat), sqr(c.greenFloat), sqr(c.blueFloat), sqr(c.alphaFloat), 0, 0, 0, 0); + } + + @Test + function testSetMultipliersRGBA() + { + color.setMultipliers(0, 0.5, 1.0, 1.5); + assertCTEquals(0, 0.5, 1.0, 1.5, 0, 0, 0, 0); + + color.reset(); + //test optional alpha args + color.setMultipliers(0, 0.25, 0.5); + assertCTEquals(0, 0.25, 0.5, 1.0, 0, 0, 0, 0); + } + + @Test + function testSetMultipliersColor() + { + color.set(0xFFBB7733); + assertCTEquals(0xBB/0xFF, 0x77/0xFF, 0x33/0xFF, 1.0, 0, 0, 0, 0); + final c:FlxColor = 0xDD995511; + // test chaining + color.set(0x0).set(c); + assertCTEquals(c.redFloat, c.greenFloat, c.blueFloat, c.alphaFloat, 0, 0, 0, 0); + } + + @Test + function testSetOffsetsRGBA() + { + color.setOffsets(0.4, 0.5, 0.6, 1.5); + assertCTEquals(1, 1, 1, 1, 0.4, 0.5, 0.6, 1.5); + + color.reset(); + //test optional alpha args + color.setOffsets(0, 0, 0).setOffsets(0.4, 0.5, 0.6); + assertCTEquals(1, 1, 1, 1, 0.4, 0.5, 0.6, 0.0); + } + + @Test + function testSetOffsetsColor() + { + color.setOffsets(0xFFBB7733); + assertCTEquals(1, 1, 1, 1, 0xBB, 0x77, 0x33, 0xFF); + final c:FlxColor = 0xDD995511; + // test chaining + color.setOffsets(0x0).setOffsets(c); + assertCTEquals(1, 1, 1, 1, c.red, c.green, c.blue, c.alpha); + } + + @Test + function testHasRGBMultipliers() + { + inline function assertHas(r,g,b, ?pos) + { + color.set(r,g,b, 0, 0, 0, 0, 0); + Assert.isTrue(color.hasRGBMultipliers(), "Expected RGB multipliers, found none", pos); + } + + inline function assertNotHas(r,g,b, ?pos) + { + color.set(r,g,b, 0, 0, 0, 0, 0); + Assert.isFalse(color.hasRGBMultipliers(), "Expected NO RGB multipliers, found none", pos); + } + + assertNotHas(1.0, 1.0, 1.0); + assertHas(0.999, 1.0, 1.0); + assertHas(1.0, 0.999, 1.0); + assertHas(1.0, 1.0, 0.999); + assertHas(1.5, 1.0, 1.0); + } + + @Test + function testHasRGBAMultipliers() + { + inline function assertHas(r,g,b,a, ?pos) + { + color.set(r,g,b,a, 0, 0, 0, 0); + Assert.isTrue(color.hasRGBAMultipliers(), "Expected RGBA multipliers, found none", pos); + } + + inline function assertNotHas(r,g,b,a, ?pos) + { + color.set(r,g,b,a, 0, 0, 0, 0); + Assert.isFalse(color.hasRGBAMultipliers(), "Expected NO RGBA multipliers, found none", pos); + } + + assertNotHas(1.0, 1.0, 1.0, 1.0); + assertHas(0.999, 1.0, 1.0, 1.0); + assertHas(1.0, 0.999, 1.0, 1.0); + assertHas(1.0, 1.0, 0.999, 1.0); + assertHas(1.0, 1.0, 1.0, 0.999); + assertHas(1.5, 1.0, 1.0, 1.0); + } + + @Test + function testHasRGBOffsets() + { + inline function assertHas(r,g,b, ?pos) + { + color.set(0, 0, 0, 0, r,g,b,0); + Assert.isTrue(color.hasRGBOffsets(), "Expected RGB offsets, found none", pos); + } + + inline function assertNotHas(r,g,b, ?pos) + { + color.set(0, 0, 0, 0, r,g,b,0); + Assert.isFalse(color.hasRGBOffsets(), "Expected NO RGB offsets, found none", pos); + } + + assertNotHas(0.0, 0.0, 0.0); + assertHas(0.001, 0.0, 0.0); + assertHas(0.0, 0.001, 0.0); + assertHas(0.0, 0.0, 0.001); + assertHas(-0.5, 0.0, 0.0); + } + + @Test + function testHasRGBAOffsets() + { + inline function assertHas(r,g,b,a, ?pos) + { + color.set(0, 0, 0, 0, r,g,b,a); + Assert.isTrue(color.hasRGBAOffsets(), "Expected RGBA offsets, found none", pos); + } + + inline function assertNotHas(r,g,b,a, ?pos) + { + color.set(0, 0, 0, 0, r,g,b,a); + Assert.isFalse(color.hasRGBAOffsets(), "Expected NO RGBA offsets, found none", pos); + } + + assertNotHas(0.0, 0.0, 0.0, 0.0); + assertHas(0.001, 0.0, 0.0, 0.0); + assertHas(0.0, 0.001, 0.0, 0.0); + assertHas(0.0, 0.0, 0.001, 0.0); + assertHas(0.0, 0.0, 0.0, 0.001); + assertHas(-0.5, 0.0, 0.0, 0.0); + } + + function assertCTEquals(rM:Float, gM:Float, bM:Float, aM:Float, rO:Float, gO:Float, bO:Float, aO:Float, margin = 0.001, ?pos:PosInfos) + { + FlxAssert.areNear(color.redMultiplier, rM, margin, 'red multiplier [${color.redMultiplier}] is not within $margin of [$rM]', pos); + FlxAssert.areNear(color.greenMultiplier, gM, margin, 'green multiplier [${color.greenMultiplier}] is not within $margin of [$gM]', pos); + FlxAssert.areNear(color.blueMultiplier, bM, margin, 'blue multiplier [${color.blueMultiplier}] is not within $margin of [$bM]', pos); + FlxAssert.areNear(color.alphaMultiplier, aM, margin, 'alpha multiplier [${color.alphaMultiplier}] is not within $margin of [$aM]', pos); + FlxAssert.areNear(color.redOffset, rO, margin, 'red offset [${color.redOffset}] is not within $margin of [$rO]', pos); + FlxAssert.areNear(color.greenOffset, gO, margin, 'green offset [${color.greenOffset}] is not within $margin of [$gO]', pos); + FlxAssert.areNear(color.blueOffset, bO, margin, 'blue offset [${color.blueOffset}] is not within $margin of [$bO]', pos); + FlxAssert.areNear(color.alphaOffset, aO, margin, 'alpha offset [${color.alphaOffset}] is not within $margin of [$aO]', pos); + } +}