diff --git a/Cocoa/Document.m b/Cocoa/Document.m index 5aa9446..dcd061f 100644 --- a/Cocoa/Document.m +++ b/Cocoa/Document.m @@ -2201,4 +2201,115 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency) { return &gb; } + +- (NSImage *)takeScreenshot +{ + NSImage *ret = nil; + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"GBFilterScreenshots"]) { + ret = [_view renderToImage]; + } + if (!ret) { + ret = [Document imageFromData:[NSData dataWithBytesNoCopy:_view.currentBuffer + length:GB_get_screen_width(&gb) * GB_get_screen_height(&gb) * 4 + freeWhenDone:false] + width:GB_get_screen_width(&gb) + height:GB_get_screen_height(&gb) + scale:1.0]; + } + [ret lockFocus]; + NSBitmapImageRep *bitmapRep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect(0, 0, + ret.size.width, ret.size.height)]; + [ret unlockFocus]; + ret = [[NSImage alloc] initWithSize:ret.size]; + [ret addRepresentation:bitmapRep]; + return ret; +} + +- (NSString *)screenshotFilename +{ + NSDate *date = [NSDate date]; + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + dateFormatter.dateStyle = NSDateFormatterLongStyle; + dateFormatter.timeStyle = NSDateFormatterMediumStyle; + return [[NSString stringWithFormat:@"%@ – %@.png", + self.fileURL.lastPathComponent.stringByDeletingPathExtension, + [dateFormatter stringFromDate:date]] stringByReplacingOccurrencesOfString:@":" withString:@"."]; // Gotta love Mac OS Classic + +} + +- (IBAction)saveScreenshot:(id)sender +{ + NSString *folder = [[NSUserDefaults standardUserDefaults] stringForKey:@"GBScreenshotFolder"]; + BOOL isDirectory = false; + if (folder) { + [[NSFileManager defaultManager] fileExistsAtPath:folder isDirectory:&isDirectory]; + } + if (!folder) { + bool shouldResume = running; + [self stop]; + NSOpenPanel *openPanel = [NSOpenPanel openPanel]; + openPanel.canChooseFiles = false; + openPanel.canChooseDirectories = true; + openPanel.message = @"Choose a folder for screenshots"; + [openPanel beginSheetModalForWindow:self.mainWindow completionHandler:^(NSInteger result) { + if (result == NSModalResponseOK) { + [[NSUserDefaults standardUserDefaults] setObject:openPanel.URL.path + forKey:@"GBScreenshotFolder"]; + [self saveScreenshot:sender]; + } + if (shouldResume) { + [self start]; + } + + }]; + return; + } + NSImage *image = [self takeScreenshot]; + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + dateFormatter.dateStyle = NSDateFormatterLongStyle; + dateFormatter.timeStyle = NSDateFormatterMediumStyle; + NSString *filename = [self screenshotFilename]; + filename = [folder stringByAppendingPathComponent:filename]; + unsigned i = 2; + while ([[NSFileManager defaultManager] fileExistsAtPath:filename]) { + filename = [[filename stringByDeletingPathExtension] stringByAppendingFormat:@" %d.png", i++]; + } + + NSBitmapImageRep *imageRep = (NSBitmapImageRep *)image.representations.firstObject; + NSData *data = [imageRep representationUsingType:NSBitmapImageFileTypePNG properties:@{}]; + [data writeToFile:filename atomically:NO]; + [self.osdView displayText:@"Screenshot saved"]; +} + +- (IBAction)saveScreenshotAs:(id)sender +{ + bool shouldResume = running; + [self stop]; + NSImage *image = [self takeScreenshot]; + NSSavePanel *savePanel = [NSSavePanel savePanel]; + [savePanel setNameFieldStringValue:[self screenshotFilename]]; + [savePanel beginSheetModalForWindow:self.mainWindow completionHandler:^(NSInteger result) { + if (result == NSModalResponseOK) { + [savePanel orderOut:self]; + NSBitmapImageRep *imageRep = (NSBitmapImageRep *)image.representations.firstObject; + NSData *data = [imageRep representationUsingType:NSBitmapImageFileTypePNG properties:@{}]; + [data writeToURL:savePanel.URL atomically:NO]; + [[NSUserDefaults standardUserDefaults] setObject:savePanel.URL.path.stringByDeletingLastPathComponent + forKey:@"GBScreenshotFolder"]; + } + if (shouldResume) { + [self start]; + } + }]; + [self.osdView displayText:@"Screenshot saved"]; +} + +- (IBAction)copyScreenshot:(id)sender +{ + NSImage *image = [self takeScreenshot]; + [[NSPasteboard generalPasteboard] clearContents]; + [[NSPasteboard generalPasteboard] writeObjects:@[image]]; + [self.osdView displayText:@"Screenshot copied"]; +} + @end diff --git a/Cocoa/GBPreferencesWindow.h b/Cocoa/GBPreferencesWindow.h index 260ebf9..e11c5d3 100644 --- a/Cocoa/GBPreferencesWindow.h +++ b/Cocoa/GBPreferencesWindow.h @@ -28,4 +28,5 @@ @property (nonatomic, weak) IBOutlet NSButton *autoUpdatesCheckbox; @property (weak) IBOutlet NSSlider *volumeSlider; @property (weak) IBOutlet NSButton *OSDCheckbox; +@property (weak) IBOutlet NSButton *screenshotFilterCheckbox; @end diff --git a/Cocoa/GBPreferencesWindow.m b/Cocoa/GBPreferencesWindow.m index 47302dd..92aca78 100644 --- a/Cocoa/GBPreferencesWindow.m +++ b/Cocoa/GBPreferencesWindow.m @@ -2,6 +2,7 @@ #import "NSString+StringForKey.h" #import "GBButtons.h" #import "BigSurToolbar.h" +#import "GBViewMetal.h" #import @implementation GBPreferencesWindow @@ -32,6 +33,7 @@ NSSlider *_volumeSlider; NSButton *_autoUpdatesCheckbox; NSButton *_OSDCheckbox; + NSButton *_screenshotFilterCheckbox; } + (NSArray *)filterList @@ -766,4 +768,27 @@ forKey:@"GBOSDEnabled"]; } + +- (IBAction)changeFilterScreenshots:(id)sender +{ + [[NSUserDefaults standardUserDefaults] setBool:[(NSButton *)sender state] == NSOnState + forKey:@"GBFilterScreenshots"]; +} + +- (NSButton *)screenshotFilterCheckbox +{ + return _screenshotFilterCheckbox; +} + +- (void)setScreenshotFilterCheckbox:(NSButton *)screenshotFilterCheckbox +{ + _screenshotFilterCheckbox = screenshotFilterCheckbox; + if (![GBViewMetal isSupported]) { + [_screenshotFilterCheckbox setEnabled:false]; + } + else { + [_screenshotFilterCheckbox setState: [[NSUserDefaults standardUserDefaults] boolForKey:@"GBFilterScreenshots"]]; + } +} + @end diff --git a/Cocoa/GBView.h b/Cocoa/GBView.h index cd6a539..a140b94 100644 --- a/Cocoa/GBView.h +++ b/Cocoa/GBView.h @@ -27,4 +27,5 @@ typedef enum { - (uint32_t *)previousBuffer; - (void)screenSizeChanged; - (void)setRumble: (double)amp; +- (NSImage *)renderToImage; @end diff --git a/Cocoa/GBView.m b/Cocoa/GBView.m index 1e584e4..bbf1a5f 100644 --- a/Cocoa/GBView.m +++ b/Cocoa/GBView.m @@ -677,4 +677,10 @@ static const uint8_t workboy_vk_to_key[] = { return false; } +- (NSImage *)renderToImage; +{ + /* Not going to support this on OpenGL, OpenGL is too much of a terrible API for me + to bother figuring out how the hell something so trivial can be done. */ + return nil; +} @end diff --git a/Cocoa/GBViewMetal.m b/Cocoa/GBViewMetal.m index 580db2c..958ad37 100644 --- a/Cocoa/GBViewMetal.m +++ b/Cocoa/GBViewMetal.m @@ -1,3 +1,4 @@ +#import #import "GBViewMetal.h" #pragma clang diagnostic ignored "-Wpartial-availability" @@ -51,8 +52,9 @@ static const vector_float2 rect[] = MTKView *view = [[MTKView alloc] initWithFrame:self.frame device:(device = MTLCreateSystemDefaultDevice())]; view.delegate = self; self.internalView = view; - view.paused = YES; - view.enableSetNeedsDisplay = YES; + view.paused = true; + view.enableSetNeedsDisplay = true; + view.framebufferOnly = false; vertices = [device newBufferWithBytes:rect length:sizeof(rect) @@ -212,4 +214,19 @@ static const vector_float2 rect[] = }); } +- (NSImage *)renderToImage +{ + CIImage *ciImage = [CIImage imageWithMTLTexture:[[(MTKView *)self.internalView currentDrawable] texture] + options:@{ + kCIImageColorSpace: (__bridge_transfer id)CGColorSpaceCreateDeviceRGB() + }]; + ciImage = [ciImage imageByApplyingTransform:CGAffineTransformTranslate(CGAffineTransformMakeScale(1, -1), + 0, ciImage.extent.size.height)]; + CIContext *context = [CIContext context]; + CGImageRef cgImage = [context createCGImage:ciImage fromRect:ciImage.extent]; + NSImage *ret = [[NSImage alloc] initWithCGImage:cgImage size:self.internalView.bounds.size]; + CGImageRelease(cgImage); + return ret; +} + @end diff --git a/Cocoa/MainMenu.xib b/Cocoa/MainMenu.xib index 04bcf8f..348e960 100644 --- a/Cocoa/MainMenu.xib +++ b/Cocoa/MainMenu.xib @@ -316,6 +316,23 @@ + + + + + + + + + + + + + + + + + diff --git a/Cocoa/Preferences.xib b/Cocoa/Preferences.xib index db309e5..6d72652 100644 --- a/Cocoa/Preferences.xib +++ b/Cocoa/Preferences.xib @@ -90,6 +90,7 @@ + @@ -98,11 +99,11 @@ - + - + @@ -111,7 +112,7 @@ - + @@ -147,6 +148,17 @@ + @@ -307,7 +319,7 @@ - +