screenshot options

This commit is contained in:
Lasercar
2025-03-24 17:32:49 -04:00
committed by Cameron Taylor
parent fc9b395138
commit c776900df0
4 changed files with 233 additions and 47 deletions
+76
View File
@@ -238,6 +238,82 @@ class Preferences
return value;
}
/**
* If enabled, the game will hide the mouse when taking a screenshot.
* @default `true`
*/
public static var shouldHideMouse(get, set):Bool;
static function get_shouldHideMouse():Bool
{
return Save?.instance?.options?.screenshot?.shouldHideMouse ?? true;
}
static function set_shouldHideMouse(value:Bool):Bool
{
var save:Save = Save.instance;
save.options.screenshot.shouldHideMouse = value;
save.flush();
return value;
}
/**
* If enabled, the game will show a preview after taking a screenshot.
* @default `true`
*/
public static var fancyPreview(get, set):Bool;
static function get_fancyPreview():Bool
{
return Save?.instance?.options?.screenshot?.fancyPreview ?? true;
}
static function set_fancyPreview(value:Bool):Bool
{
var save:Save = Save.instance;
save.options.screenshot.fancyPreview = value;
save.flush();
return value;
}
/**
* If enabled, the game will save any screenshots taken to this format.
* @default `PNG`
*/
public static var saveFormat(get, set):Any;
static function get_saveFormat():Any
{
return Save?.instance?.options?.screenshot?.saveFormat ?? 'PNG';
}
static function set_saveFormat(value):Any
{
var save:Save = Save.instance;
save.options.screenshot.saveFormat = value;
save.flush();
return value;
}
/**
* If enabled, the game will save JEPG screenshots with this quality percentage.
* @default `80`
*/
public static var jpegQuality(get, set):Int;
static function get_jpegQuality():Int
{
return Save?.instance?.options?.screenshot?.jpegQuality ?? 80;
}
static function set_jpegQuality(value:Int):Int
{
var save:Save = Save.instance;
save.options.screenshot.jpegQuality = value;
save.flush();
return value;
}
/**
* Loads the user's preferences from the save data and apply them.
*/
+21
View File
@@ -107,6 +107,14 @@ class Save
audioVisualOffset: 0,
unlockedFramerate: false,
screenshot:
{
shouldHideMouse: true,
fancyPreview: true,
saveFormat: 'PNG',
jpegQuality: 80,
},
controls:
{
// Leave controls blank so defaults are loaded.
@@ -1388,6 +1396,19 @@ typedef SaveDataOptions =
*/
var unlockedFramerate:Bool;
// screenshot options
var screenshot:
{
// Should the mouse be hidden when taking a screenshot?
var shouldHideMouse:Bool;
// Show a fancy preview
var fancyPreview:Bool;
// The save format of the screenshot, PNG or JPEG
var saveFormat:String;
// The JPEG Quality, if we're saving to the format
var jpegQuality:Int;
};
var controls:
{
var p1:
@@ -119,6 +119,19 @@ class PreferencesMenu extends Page
Preferences.autoFullscreen = value;
}, Preferences.autoFullscreen);
createPrefItemCheckbox('Hide Mouse', 'Enable to hide the mouse when taking a screenshot', function(value:Bool):Void {
Preferences.shouldHideMouse = value;
}, Preferences.shouldHideMouse);
createPrefItemCheckbox('Fancy Preview', 'Enable to show a preview after taking a screenshot', function(value:Bool):Void {
Preferences.fancyPreview = value;
}, Preferences.fancyPreview);
createPrefItemEnum('Save Format', 'Save screenshots to this format', ['PNG' => 'PNG', 'JPEG' => 'JPEG'], function(value:String):Void {
Preferences.saveFormat = value;
}, Preferences.saveFormat);
createPrefItemNumber('JPEG Quality', 'The quality of JPEG screenshots', function(value:Float) {
Preferences.jpegQuality = Std.int(value);
}, null, Preferences.jpegQuality, 0, 100, 5, 0);
#if web
createPrefItemCheckbox('Unlocked Framerate', 'Enable to unlock the framerate', function(value:Bool):Void {
Preferences.unlockedFramerate = value;
+123 -47
View File
@@ -20,14 +20,12 @@ import openfl.display.PNGEncoderOptions;
import openfl.geom.Rectangle;
import openfl.utils.ByteArray;
import openfl.events.MouseEvent;
import funkin.Preferences;
typedef ScreenshotPluginParams =
{
hotkeys:Array<FlxKey>,
?region:Rectangle,
shouldHideMouse:Bool,
flashColor:Null<FlxColor>,
fancyPreview:Bool,
};
/**
@@ -36,17 +34,37 @@ typedef ScreenshotPluginParams =
*/
class ScreenshotPlugin extends FlxBasic
{
/**
* Current `ScreenshotPlugin` instance
*/
public static var instance:ScreenshotPlugin = null;
public static final SCREENSHOT_FOLDER = 'screenshots';
var _hotkeys:Array<FlxKey>;
var region:Null<Rectangle>;
var _region:Null<Rectangle>;
/**
* The color used for the flash
*/
public static var flashColor(default, set):Int = 0xFFFFFFFF;
var _shouldHideMouse:Bool;
public static function set_flashColor(v:Int):Int
{
flashColor = v;
if (instance != null && instance.flashBitmap != null) instance.flashBitmap.bitmapData = new BitmapData(lastWidth, lastHeight, true, v);
return flashColor;
}
var _flashColor:Null<FlxColor>;
/**
* The save format used to save the screenshots, default to `PNG`.
* The current available formats are `PNG` and `JPEG`
*/
public static var saveFormat(get, null):FileFormatOption;
var _fancyPreview:Bool;
public static function get_saveFormat()
{
return Preferences.saveFormat;
}
/**
* A signal fired before the screenshot is taken.
@@ -66,7 +84,7 @@ class ScreenshotPlugin extends FlxBasic
var flashSprite:Sprite;
var flashBitmap:Bitmap;
var previewSprite:Sprite;
var shotDisplayBitmap:Bitmap;
var shotPreviewBitmap:Bitmap;
var outlineBitmap:Bitmap;
var wasMouseHidden = false;
@@ -89,6 +107,14 @@ class ScreenshotPlugin extends FlxBasic
{
super();
if (instance != null)
{
destroy();
return;
}
instance = this;
lastWidth = FlxG.width;
lastHeight = FlxG.height;
@@ -109,17 +135,14 @@ class ScreenshotPlugin extends FlxBasic
outlineBitmap.y = 5;
previewSprite.addChild(outlineBitmap);
shotDisplayBitmap = new Bitmap();
shotDisplayBitmap.scaleX /= 5;
shotDisplayBitmap.scaleY /= 5;
previewSprite.addChild(shotDisplayBitmap);
shotPreviewBitmap = new Bitmap();
shotPreviewBitmap.scaleX /= 5;
shotPreviewBitmap.scaleY /= 5;
previewSprite.addChild(shotPreviewBitmap);
containerThing.addChild(flashSprite);
_hotkeys = params.hotkeys;
_region = params.region ?? null;
_shouldHideMouse = params.shouldHideMouse;
_flashColor = params.flashColor;
_fancyPreview = params.fancyPreview;
region = params.region ?? null;
flashColor = params.flashColor;
onPreScreenshot = new FlxTypedSignal<Void->Void>();
onPostScreenshot = new FlxTypedSignal<Bitmap->Void>();
@@ -137,7 +160,7 @@ class ScreenshotPlugin extends FlxBasic
}
else
{
// if the loop has been started, and is finished, then we swich which groups are active
// if the loop has been started, and is finished, then we kill. it
if (asyncLoop.finished)
{
trace("finished saving screenshot buffer");
@@ -155,33 +178,46 @@ class ScreenshotPlugin extends FlxBasic
if (hasPressedScreenshot())
{
if (_shouldHideMouse && !wasMouseHidden && FlxG.mouse.visible)
if (Preferences.shouldHideMouse && !wasMouseHidden && FlxG.mouse.visible)
{
wasMouseHidden = true;
// argh, even though this is happening, it's not happening fast enough before the screenshot is taken!
// Whatever, I made the popup not show up, that's all I need
// If I just have enough things here and run this first, it'll eventually work, right?
Cursor.hide();
}
if (FlxG.keys.pressed.SHIFT)
{
// If there's no preview and we have the option enabled we're not going to bother opening the folder
if (containerThing.alpha == 0 && Preferences.fancyPreview == true || previewSprite.alpha == 0 && Preferences.fancyPreview == true) return;
else
{
openScreenshotsFolder();
return; // We're only opening the screenshots folder (we don't want to accidently take a screenshot after this)
}
}
// screenshot spamming timer
if (screenshotSpammedTimer == null || screenshotSpammedTimer.finished == true)
{
screenshotSpammedTimer = new FlxTimer().start(1, function(_) {
// The player's stopped spamming shots, so we can stop the screenshot spam mode too
containerThing.alpha = 1;
screenshotBeingSpammed = false;
if (screenshotBuffer[0] != null) saveBufferedScreenshots(screenshotBuffer, screenshotNameBuffer);
if (_shouldHideMouse && wasMouseHidden && !FlxG.mouse.visible)
if (wasMouseHidden && !FlxG.mouse.visible)
{
wasMouseHidden = false;
Cursor.show();
}
});
}
else
else // Pressing the screenshot key more than once every second enables the screenshot spam mode and resets the timer
{
screenshotBeingSpammed = true;
containerThing.alpha = 0;
screenshotSpammedTimer.reset(1);
}
capture();
capture(); // After all these checks, we finally try taking a screenshot
}
}
@@ -193,11 +229,6 @@ class ScreenshotPlugin extends FlxBasic
FlxG.plugins.addPlugin(new ScreenshotPlugin(
{
flashColor: Preferences.flashingLights ? FlxColor.WHITE : null, // Was originally a black flash.
// TODO: Add a way to configure screenshots from the options menu.
hotkeys: [FlxKey.F3],
shouldHideMouse: true,
fancyPreview: true,
}));
}
@@ -210,16 +241,16 @@ class ScreenshotPlugin extends FlxBasic
#end
}
public function updatePreferences():Void
public function updateFlashColor():Void
{
_flashColor = Preferences.flashingLights ? FlxColor.WHITE : null;
Preferences.flashingLights ? set_flashColor(FlxColor.WHITE) : null;
}
private function resizeBitmap(width:Int, height:Int)
{
lastWidth = width;
lastHeight = height;
flashBitmap.bitmapData = new BitmapData(lastWidth, lastHeight, true, _flashColor);
flashBitmap.bitmapData = new BitmapData(lastWidth, lastHeight, true, flashColor);
outlineBitmap.bitmapData = new BitmapData(Std.int(lastWidth / 5) + 10, Std.int(lastHeight / 5) + 10, true, 0xFFFFFFFF);
}
@@ -229,7 +260,7 @@ class ScreenshotPlugin extends FlxBasic
*/
public function defineCaptureRegion(x:Int, y:Int, width:Int, height:Int):Void
{
_region = new Rectangle(x, y, width, height);
region = new Rectangle(x, y, width, height);
}
/**
@@ -239,7 +270,7 @@ class ScreenshotPlugin extends FlxBasic
{
onPreScreenshot.dispatch();
var captureRegion = _region != null ? _region : new Rectangle(0, 0, FlxG.stage.stageWidth, FlxG.stage.stageHeight);
// var captureRegion = region != null ? region : new Rectangle(0, 0, FlxG.stage.stageWidth, FlxG.stage.stageHeight);
// The actual work.
// var bitmap = new Bitmap(new BitmapData(Math.floor(captureRegion.width), Math.floor(captureRegion.height), true, 0x00000000)); // Create a transparent empty bitmap.
@@ -274,7 +305,7 @@ class ScreenshotPlugin extends FlxBasic
showFancyPreview(shot);
if (_shouldHideMouse && wasMouseHidden && !FlxG.mouse.visible)
if (wasMouseHidden && !FlxG.mouse.visible)
{
wasMouseHidden = false;
Cursor.show();
@@ -322,9 +353,10 @@ class ScreenshotPlugin extends FlxBasic
function showFancyPreview(shot:Bitmap):Void
{
shotDisplayBitmap.bitmapData = shot.bitmapData;
shotDisplayBitmap.x = outlineBitmap.x + 5;
shotDisplayBitmap.y = outlineBitmap.y + 5;
if (!Preferences.fancyPreview) return; // Sorry, the previews' been cancelled
shotPreviewBitmap.bitmapData = shot.bitmapData;
shotPreviewBitmap.x = outlineBitmap.x + 5;
shotPreviewBitmap.y = outlineBitmap.y + 5;
previewSprite.alpha = 1;
FlxTween.tween(previewSprite, {alpha: 0}, 0.5, {startDelay: .5});
@@ -440,7 +472,7 @@ class ScreenshotPlugin extends FlxBasic
*/
}
function openScreenshotsFolder(e:MouseEvent):Void
function openScreenshotsFolder():Void
{
FileUtil.openFolder(SCREENSHOT_FOLDER);
}
@@ -466,11 +498,11 @@ class ScreenshotPlugin extends FlxBasic
}
/**
* Convert a Bitmap to a PNG ByteArray to save to a file.
* Convert a Bitmap to a PNG or JPEG ByteArray to save to a file.
*/
static function encodePNG(bitmap:Bitmap):ByteArray
static function encode(bitmap:Bitmap):ByteArray
{
return bitmap.bitmapData.encode(bitmap.bitmapData.rect, new PNGEncoderOptions());
return bitmap.bitmapData.encode(bitmap.bitmapData.rect, saveFormat.returnEncoder());
}
var previousScreenshotName:String;
@@ -487,7 +519,8 @@ class ScreenshotPlugin extends FlxBasic
if (previousScreenshotName != targetPath)
{
previousScreenshotName = targetPath;
targetPath = getScreenshotPath() + targetPath + '.png';
targetPath = getScreenshotPath() + targetPath + '.' + Std.string(saveFormat)
.toLowerCase(); // This is stupid, I'd pefer it did this in the FileFormatOption but idk how
previousScreenshotCopyNum = 2;
trace(previousScreenshotName);
trace(targetPath);
@@ -501,13 +534,13 @@ class ScreenshotPlugin extends FlxBasic
previousScreenshotCopyNum++;
}
previousScreenshotName = newTargetPath;
targetPath = getScreenshotPath() + newTargetPath + '.png';
targetPath = getScreenshotPath() + newTargetPath + '.' + Std.string(saveFormat).toLowerCase();
}
var pngData = encodePNG(bitmap);
var pngData = encode(bitmap);
if (pngData == null)
{
trace('[WARN] Failed to encode PNG data.');
trace('[WARN] Failed to encode ${saveFormat} data');
previousScreenshotName = null;
return;
}
@@ -525,11 +558,12 @@ class ScreenshotPlugin extends FlxBasic
}
}
// I'm very happy with this code, all of it just works - Lasercar
// I' m very happy with this code, all of it just works - Lasercar function
function saveBufferedScreenshots(screenshots:Array<Bitmap>, screenshotNames)
{
trace('Saving screenshot buffer');
var i:Int = 0;
asyncLoop = new FlxAsyncLoop(screenshots.length, () -> {
if (screenshots[i] != null)
{
@@ -539,9 +573,51 @@ class ScreenshotPlugin extends FlxBasic
}, 1);
// for (i in 0...screenshots.length)
// {
// }
getCurrentState().add(asyncLoop);
showFancyPreview(screenshots[screenshots.length - 1]); // show the preview for the last screenshot
}
override public function destroy():Void
{
if (instance == this) instance = null;
if (FlxG.plugins.list.contains(this)) FlxG.plugins.remove(this);
FlxG.signals.gameResized.remove(this.resizeBitmap);
FlxG.stage.removeChild(containerThing);
super.destroy();
if (containerThing == null) return;
@:privateAccess
for (parent in [containerThing, flashSprite, previewSprite])
for (child in parent.__children)
parent.removeChild(child);
containerThing = null;
flashSprite = null;
flashBitmap = null;
previewSprite = null;
shotPreviewBitmap = null;
outlineBitmap = null;
}
}
enum abstract FileFormatOption(String) from String
{
var JPEG = ".jpg";
var PNG = ".png";
public function returnEncoder():Any
{
return switch (this : FileFormatOption)
{
// I have to make this check the string value rather than the var because I can't get it to work as a var
// JPEG encoder causes the game to crash?????
// case 'JPEG': new openfl.display.JPEGEncoderOptions(Preferences.jpegQuality);
default: new openfl.display.JPEGEncoderOptions();
}
}
}