Added video rendering

This commit is contained in:
Akop Karapetyan 2019-10-19 23:37:38 -07:00 committed by tmaul
parent fdc1141f4c
commit 501d815a31
9 changed files with 687 additions and 64 deletions

View File

@ -975,7 +975,8 @@
FE1B287B23561A7A0065200C /* sshot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE1B239E23561A750065200C /* sshot.cpp */; };
FE1B288F23562B360065200C /* m68kops.c in Sources */ = {isa = PBXBuildFile; fileRef = FE1B288E23562B360065200C /* m68kops.c */; };
FEA5E79223563F5400DA2D9D /* misc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FEA5E79023563F5400DA2D9D /* misc.cpp */; };
FEA5E79A2356472200DA2D9D /* vid_macos.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FEA5E7992356472200DA2D9D /* vid_macos.cpp */; };
FEC5D3D6235C136F00ABA9FB /* FBVideo.mm in Sources */ = {isa = PBXBuildFile; fileRef = FEC5D3D5235C136F00ABA9FB /* FBVideo.mm */; };
FEC5D3D9235C160600ABA9FB /* FBScreenView.mm in Sources */ = {isa = PBXBuildFile; fileRef = FEC5D3D8235C160600ABA9FB /* FBScreenView.mm */; };
FEED9DCA2356EF5000B7AF83 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FEED9DC92356EF4F00B7AF83 /* OpenGL.framework */; };
FEED9DD12356F0B900B7AF83 /* SDL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FEED9DCF2356F07500B7AF83 /* SDL.framework */; };
FEED9DD22356F0B900B7AF83 /* SDL.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = FEED9DCF2356F07500B7AF83 /* SDL.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@ -2465,7 +2466,6 @@
FE1B288D23562B350065200C /* m68kops.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = m68kops.h; sourceTree = "<group>"; };
FE1B288E23562B360065200C /* m68kops.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = m68kops.c; sourceTree = "<group>"; };
FEA5E79023563F5400DA2D9D /* misc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = misc.cpp; sourceTree = "<group>"; };
FEA5E7992356472200DA2D9D /* vid_macos.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = vid_macos.cpp; sourceTree = "<group>"; };
FEA5E79C23564A3200DA2D9D /* cave_tile_func.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = cave_tile_func.pl; sourceTree = "<group>"; };
FEA5E7AE23564A6400DA2D9D /* cave_sprite_func.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = cave_sprite_func.pl; sourceTree = "<group>"; };
FEA5E7B623566E7600DA2D9D /* cave_tile_func_table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cave_tile_func_table.h; sourceTree = "<group>"; };
@ -2475,6 +2475,10 @@
FEA5E7BD2356721800DA2D9D /* neo_sprite_func.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = neo_sprite_func.pl; sourceTree = "<group>"; };
FEA5E7BE2356721800DA2D9D /* psikyo_tile_func.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = psikyo_tile_func.pl; sourceTree = "<group>"; };
FEA5E7BF2356721900DA2D9D /* toa_gp9001_func.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = toa_gp9001_func.pl; sourceTree = "<group>"; };
FEC5D3D4235C136E00ABA9FB /* FBVideo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBVideo.h; sourceTree = "<group>"; };
FEC5D3D5235C136F00ABA9FB /* FBVideo.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FBVideo.mm; sourceTree = "<group>"; };
FEC5D3D7235C160600ABA9FB /* FBScreenView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBScreenView.h; sourceTree = "<group>"; };
FEC5D3D8235C160600ABA9FB /* FBScreenView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FBScreenView.mm; sourceTree = "<group>"; };
FEED9DC22356DDA900B7AF83 /* gamelist.pl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.perl; path = gamelist.pl; sourceTree = "<group>"; };
FEED9DC92356EF4F00B7AF83 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; };
FEED9DCF2356F07500B7AF83 /* SDL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL.framework; path = ../../../../../../Library/Frameworks/SDL.framework; sourceTree = "<group>"; };
@ -4617,7 +4621,10 @@
FEA5E7982356472200DA2D9D /* macos */ = {
isa = PBXGroup;
children = (
FEA5E7992356472200DA2D9D /* vid_macos.cpp */,
FEC5D3D7235C160600ABA9FB /* FBScreenView.h */,
FEC5D3D8235C160600ABA9FB /* FBScreenView.mm */,
FEC5D3D4235C136E00ABA9FB /* FBVideo.h */,
FEC5D3D5235C136F00ABA9FB /* FBVideo.mm */,
);
path = macos;
sourceTree = "<group>";
@ -4929,6 +4936,7 @@
FEED9E0623576DD800B7AF83 /* ips_manager.cpp in Sources */,
FE1B272123561A780065200C /* d_bogeyman.cpp in Sources */,
FE1B24B323561A750065200C /* d_m72.cpp in Sources */,
FEC5D3D6235C136F00ABA9FB /* FBVideo.mm in Sources */,
FE1B268D23561A770065200C /* d_thief.cpp in Sources */,
FE1B266A23561A770065200C /* tc0280grd.cpp in Sources */,
FE1B254823561A760065200C /* d_dooyong.cpp in Sources */,
@ -5327,6 +5335,7 @@
FE1B26FF23561A780065200C /* d_seicross.cpp in Sources */,
FE1B245B23561A750065200C /* gzwrite.c in Sources */,
FE1B278223561A790065200C /* tms5110.cpp in Sources */,
FEC5D3D9235C160600ABA9FB /* FBScreenView.mm in Sources */,
FE1B26A223561A770065200C /* d_snk.cpp in Sources */,
FE1B271723561A780065200C /* d_travrusa.cpp in Sources */,
FE1B242523561A750065200C /* Lzma2Dec.c in Sources */,
@ -5506,7 +5515,6 @@
FE1B26B623561A770065200C /* d_mrdo.cpp in Sources */,
FE1B27DD23561A790065200C /* mathbox.cpp in Sources */,
FE1B268E23561A770065200C /* d_bionicc.cpp in Sources */,
FEA5E79A2356472200DA2D9D /* vid_macos.cpp in Sources */,
FE1B24BF23561A750065200C /* smssystem.cpp in Sources */,
FE1B267623561A770065200C /* taito_ic.cpp in Sources */,
FE1B26BF23561A770065200C /* d_wallc.cpp in Sources */,

View File

@ -8,7 +8,16 @@
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate>
#import "FBScreenView.h"
@class FBVideo;
@interface AppDelegate : NSObject <NSApplicationDelegate, FBScreenViewDelegate>
{
IBOutlet FBScreenView *screen;
}
+ (AppDelegate *) sharedInstance;
@property (readonly) FBVideo *video;
@end

View File

@ -1,4 +1,4 @@
//(
//
// AppDelegate.m
// FinalBurnNeo
//
@ -9,21 +9,38 @@
#import "AppDelegate.h"
#import "FBMainThread.h"
#import "FBVideo.h"
@interface AppDelegate ()
@property (weak) IBOutlet NSWindow *window;
- (NSSize) gameScreenSize;
@end
static AppDelegate *sharedInstance = nil;
@implementation AppDelegate
{
FBMainThread *main;
BOOL _cursorVisible;
}
- (void)dealloc
{
screen.delegate = nil;
}
- (void) awakeFromNib
{
sharedInstance = self;
_video = [FBVideo new];
main = [FBMainThread new];
_cursorVisible = YES;
screen.delegate = self;
_video.delegate = screen;
}
- (void) applicationDidFinishLaunching:(NSNotification *)aNotification {
@ -42,6 +59,47 @@
[main cancel];
}
- (NSSize) windowWillResize:(NSWindow *) sender
toSize:(NSSize) frameSize
{
NSSize screenSize = [self gameScreenSize];
NSRect windowFrame = [[self window] frame];
NSView *contentView = [[self window] contentView];
NSRect viewRect = [contentView convertRect:[contentView bounds]
toView:nil];
NSRect contentRect = [[self window] contentRectForFrameRect:windowFrame];
CGFloat screenRatio = screenSize.width / screenSize.height;
float marginY = viewRect.origin.y + windowFrame.size.height - contentRect.size.height;
float marginX = contentRect.size.width - viewRect.size.width;
// Clamp the minimum height
if ((frameSize.height - marginY) < screenSize.height) {
frameSize.height = screenSize.height + marginY;
}
// Set the screen width as a percentage of the screen height
frameSize.width = (frameSize.height - marginY) * screenRatio + marginX;
return frameSize;
}
- (void) windowDidResize:(NSNotification *) notification
{
NSSize screenSize = [self gameScreenSize];
if (screenSize.width != 0 && screenSize.height != 0) {
NSRect windowFrame = [[self window] frame];
NSRect contentRect = [[self window] contentRectForFrameRect:windowFrame];
NSString *screenSizeString = NSStringFromSize(screenSize);
NSString *actualSizeString = NSStringFromSize(contentRect.size);
[[NSUserDefaults standardUserDefaults] setObject:actualSizeString
forKey:[@"preferredSize-" stringByAppendingString:screenSizeString]];
}
}
- (BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication *) sender
{
return YES;
@ -54,4 +112,38 @@
return YES;
}
+ (AppDelegate *) sharedInstance
{
return sharedInstance;
}
#pragma mark - FBScreenViewDelegate
- (void) mouseDidIdle
{
if (_cursorVisible) {
_cursorVisible = NO;
[NSCursor hide];
}
}
- (void) mouseStateDidChange
{
if (!_cursorVisible) {
_cursorVisible = YES;
[NSCursor unhide];
}
}
#pragma mark - Private
extern int BurnDrvGetVisibleSize(int* pnWidth, int* pnHeight);
- (NSSize) gameScreenSize
{
int w, h;
BurnDrvGetVisibleSize(&w, &h);
return NSMakeSize(w, h);
}
@end

View File

@ -12,9 +12,10 @@
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate">
<connections>
<outlet property="screen" destination="Jj9-3G-U07" id="7Fv-Ip-Vmf"/>
<outlet property="window" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
</connections>
</customObject>
@ -689,11 +690,17 @@
<view key="contentView" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<openGLView wantsLayer="YES" fixedFrame="YES" colorSize="5bit_RGB_8bit_Alpha" depthSize="32bit" rendererType="accelerated" useAuxiliaryDepthBufferStencil="NO" useDoubleBufferingEnabled="YES" allowOffline="YES" useRecovery="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Jj9-3G-U07" customClass="FBScreenView">
<rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</openGLView>
</subviews>
</view>
<connections>
<outlet property="delegate" destination="Voe-Tx-rLC" id="suI-T0-B1U"/>
</connections>
<point key="canvasLocation" x="140" y="-120"/>
<point key="canvasLocation" x="131" y="-153"/>
</window>
</objects>
</document>

View File

@ -0,0 +1,26 @@
// Portions from FinalBurn X: Port of FinalBurn to OS X
// https://github.com/0xe1f/FinalBurn-X
//
// Copyright (C) Akop Karapetyan
// Licensed under the Apache License, Version 2.0 (the "License");
// http://www.apache.org/licenses/LICENSE-2.0
#import <Cocoa/Cocoa.h>
#import "FBVideo.h"
@protocol FBScreenViewDelegate<NSObject>
@optional
- (void) mouseDidIdle;
- (void) mouseStateDidChange;
@end
@interface FBScreenView : NSOpenGLView<FBVideoDelegate>
- (NSSize) screenSize;
@property (nonatomic, weak) id<FBScreenViewDelegate> delegate;
@end

View File

@ -0,0 +1,325 @@
// Portions from FinalBurn X: Port of FinalBurn to OS X
// https://github.com/0xe1f/FinalBurn-X
//
// Copyright (C) Akop Karapetyan
// Licensed under the Apache License, Version 2.0 (the "License");
// http://www.apache.org/licenses/LICENSE-2.0
#include <OpenGL/gl.h>
#include <time.h>
#import "FBScreenView.h"
#define HIDE_CURSOR_TIMEOUT_SECONDS 1.0f
@interface FBScreenView ()
+ (int) powerOfTwoClosestTo:(int) number;
- (void) resetProjection;
- (void) handleMouseChange:(NSPoint) position;
- (void) resetTrackingArea;
@end
@implementation FBScreenView
{
GLuint screenTextureId;
unsigned char *texture;
int imageWidth;
int imageHeight;
BOOL isRotated;
int textureWidth;
int textureHeight;
int textureBytesPerPixel;
NSLock *renderLock;
NSSize screenSize;
CFAbsoluteTime _lastMouseAction;
NSPoint _lastCursorPosition;
NSTrackingArea *_trackingArea;
NSRect viewBounds; // Access from non-UI thread
}
#pragma mark - Initialize, Dealloc
- (void) dealloc
{
glDeleteTextures(1, &screenTextureId);
free(self->texture);
}
- (void) awakeFromNib
{
self->renderLock = [[NSLock alloc] init];
_lastMouseAction = CFAbsoluteTimeGetCurrent();
_lastCursorPosition = NSMakePoint(-1, -1);
[self resetTrackingArea];
}
#pragma mark - Cocoa Callbacks
- (void) prepareOpenGL
{
[super prepareOpenGL];
NSLog(@"FBScreenView/prepareOpenGL");
[self->renderLock lock];
// Synchronize buffer swaps with vertical refresh rate
GLint swapInt = 1;
[[self openGLContext] setValues:&swapInt
forParameter:NSOpenGLCPSwapInterval];
glClearColor(0, 0, 0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glGenTextures(1, &screenTextureId);
[self->renderLock unlock];
}
- (BOOL) acceptsFirstResponder
{
return YES;
}
- (void) drawRect:(NSRect)dirtyRect
{
[self->renderLock lock];
NSOpenGLContext *nsContext = [self openGLContext];
[nsContext makeCurrentContext];
glClear(GL_COLOR_BUFFER_BIT);
GLfloat coordX = (GLfloat)self->imageWidth / self->textureWidth;
GLfloat coordY = (GLfloat)self->imageHeight / self->textureHeight;
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, screenTextureId);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
NSSize size = [self bounds].size;
CGFloat offset = 0;
glBegin(GL_QUADS);
glTexCoord2f(0.0, 0.0);
glVertex3f(-offset, 0.0, 0.0);
glTexCoord2f(coordX, 0.0);
glVertex3f(size.width + offset, 0.0, 0.0);
glTexCoord2f(coordX, coordY);
glVertex3f(size.width + offset, size.height, 0.0);
glTexCoord2f(0.0, coordY);
glVertex3f(-offset, size.height, 0.0);
glEnd();
glDisable(GL_TEXTURE_2D);
[nsContext flushBuffer];
[self->renderLock unlock];
}
- (void)reshape
{
viewBounds = [self bounds];
[self->renderLock lock];
[[self openGLContext] makeCurrentContext];
[[self openGLContext] update];
[self resetProjection];
glClear(GL_COLOR_BUFFER_BIT);
[self->renderLock unlock];
}
#pragma mark - FBVideoRenderDelegate
- (void)initTextureOfWidth:(int)width
height:(int)height
isRotated:(BOOL)rotated
bytesPerPixel:(int)bytesPerPixel
{
NSLog(@"FBScreenView/initTexture");
[self->renderLock lock];
NSOpenGLContext *nsContext = [self openGLContext];
[nsContext makeCurrentContext];
free(self->texture);
self->imageWidth = width;
self->imageHeight = height;
self->isRotated = rotated;
self->textureWidth = [FBScreenView powerOfTwoClosestTo:self->imageWidth];
self->textureHeight = [FBScreenView powerOfTwoClosestTo:self->imageHeight];
self->textureBytesPerPixel = bytesPerPixel;
self->screenSize = NSMakeSize((CGFloat)width, (CGFloat)height);
int texSize = self->textureWidth * self->textureHeight * bytesPerPixel;
self->texture = (unsigned char *)malloc(texSize);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, screenTextureId);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, bytesPerPixel,
self->textureWidth, self->textureHeight,
0, GL_BGR, GL_UNSIGNED_BYTE, self->texture);
glDisable(GL_TEXTURE_2D);
[self resetProjection];
[self->renderLock unlock];
}
- (void)renderFrame:(unsigned char *)bitmap
{
if (NSPointInRect(_lastCursorPosition, viewBounds)) {
CFAbsoluteTime interval = CFAbsoluteTimeGetCurrent() - _lastMouseAction;
if (interval > HIDE_CURSOR_TIMEOUT_SECONDS) {
if ([_delegate respondsToSelector:@selector(mouseDidIdle)]) {
[_delegate mouseDidIdle];
}
_lastCursorPosition.x = -1;
}
}
[self->renderLock lock];
NSOpenGLContext *nsContext = [self openGLContext];
[nsContext makeCurrentContext];
glClear(GL_COLOR_BUFFER_BIT);
GLfloat coordX = (GLfloat)self->imageWidth / self->textureWidth;
GLfloat coordY = (GLfloat)self->imageHeight / self->textureHeight;
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, screenTextureId);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
for (int y = 0; y < self->imageHeight; y += 1) {
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, y, self->imageWidth, 1,
GL_BGR, GL_UNSIGNED_BYTE,
bitmap + y * self->imageWidth * self->textureBytesPerPixel);
}
NSSize size = viewBounds.size;
CGFloat offset = 0;
glBegin(GL_QUADS);
glTexCoord2f(0.0, 0.0);
glVertex3f(-offset, 0.0, 0.0);
glTexCoord2f(coordX, 0.0);
glVertex3f(size.width + offset, 0.0, 0.0);
glTexCoord2f(coordX, coordY);
glVertex3f(size.width + offset, size.height, 0.0);
glTexCoord2f(0.0, coordY);
glVertex3f(-offset, size.height, 0.0);
glEnd();
glDisable(GL_TEXTURE_2D);
[nsContext flushBuffer];
[self->renderLock unlock];
}
#pragma mark - Mouse tracking
- (void) mouseEntered:(NSEvent *) event
{
[self handleMouseChange:[event locationInWindow]];
}
- (void) mouseMoved:(NSEvent *) event
{
[self handleMouseChange:[event locationInWindow]];
}
- (void) mouseExited:(NSEvent *) event
{
[self handleMouseChange:NSMakePoint(-1, -1)];
}
- (void) mouseDown:(NSEvent *) event
{
[self handleMouseChange:[event locationInWindow]];
}
- (void) rightMouseDown:(NSEvent *) event
{
[self handleMouseChange:[event locationInWindow]];
}
#pragma mark - Public methods
- (NSSize) screenSize
{
return self->screenSize;
}
#pragma mark - Private methods
- (void) resetTrackingArea
{
if (_trackingArea) {
[self removeTrackingArea:_trackingArea];
}
NSTrackingAreaOptions opts = NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveInKeyWindow | NSTrackingInVisibleRect;
_trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect
options:opts
owner:self
userInfo:nil];
[self addTrackingArea:_trackingArea];
}
- (void) handleMouseChange:(NSPoint) position
{
_lastMouseAction = CFAbsoluteTimeGetCurrent();
_lastCursorPosition = [self convertPoint:position
fromView:nil];
if ([_delegate respondsToSelector:@selector(mouseStateDidChange)]) {
[_delegate mouseStateDidChange];
}
}
- (void)resetProjection
{
NSSize size = [self bounds].size;
glViewport(0, 0, size.width, size.height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (!self->isRotated) {
glOrtho(0, size.width, size.height, 0, -1, 1);
}
else
{
glRotatef(90.0, 0.0, 0.0, 1.0);
glOrtho(0, size.width, size.height, 0, -1, 5);
}
glMatrixMode(GL_MODELVIEW);
}
+ (int)powerOfTwoClosestTo:(int)number
{
int rv = 1;
while (rv < number) rv *= 2;
return rv;
}
@end

View File

@ -0,0 +1,26 @@
// Portions from FinalBurn X: Port of FinalBurn to OS X
// https://github.com/0xe1f/FinalBurn-X
//
// Copyright (C) Akop Karapetyan
// Licensed under the Apache License, Version 2.0 (the "License");
// http://www.apache.org/licenses/LICENSE-2.0
#import <Foundation/Foundation.h>
@protocol FBVideoDelegate<NSObject>
@optional
- (void)screenSizeDidChange:(NSSize)newSize;
- (void)initTextureOfWidth:(int)width
height:(int)height
isRotated:(BOOL)rotated
bytesPerPixel:(int)bytesPerPixel;
- (void)renderFrame:(unsigned char *)bitmap;
@end
@interface FBVideo : NSObject
@property (nonatomic, weak) id<FBVideoDelegate> delegate;
@end

View File

@ -0,0 +1,186 @@
// Portions from FinalBurn X: Port of FinalBurn to OS X
// https://github.com/0xe1f/FinalBurn-X
//
// Copyright (C) Akop Karapetyan
// Licensed under the Apache License, Version 2.0 (the "License");
// http://www.apache.org/licenses/LICENSE-2.0
#import "FBVideo.h"
#import "AppDelegate.h"
#include "burner.h"
@implementation FBVideo
#pragma mark - Init and dealloc
- (instancetype) init
{
if (self = [super init]) {
}
return self;
}
- (void) dealloc
{
_delegate = nil;
}
#pragma mark - Intermediaries
- (BOOL) renderToSurface:(unsigned char *) buffer
{
id<FBVideoDelegate> vd = [self delegate];
if ([vd respondsToSelector:@selector(renderFrame:)])
[vd renderFrame:buffer];
return YES;
}
- (void) notifyTextureReadyOfWidth:(int) texWidth
height:(int) texHeight
isRotated:(BOOL) isRotated
bytesPerPixel:(int) bpp
screenSize:(NSSize) size
{
dispatch_async(dispatch_get_main_queue(), ^{
id<FBVideoDelegate> vd = [self delegate];
if ([vd respondsToSelector:@selector(initTextureOfWidth:height:isRotated:bytesPerPixel:)])
[vd initTextureOfWidth:texWidth
height:texHeight
isRotated:isRotated
bytesPerPixel:bpp];
if ([vd respondsToSelector:@selector(screenSizeDidChange:)])
[vd screenSizeDidChange:size];
});
}
@end
#pragma mark - FinalBurn callbacks
static unsigned char *screenBuffer = NULL;
static int bufferWidth = 0;
static int bufferHeight = 0;
static int bufferBytesPerPixel = 0;
static int MacOSVideoInit()
{
int gameWidth;
int gameHeight;
int rotationMode = 0;
BurnDrvGetVisibleSize(&gameWidth, &gameHeight);
if (BurnDrvGetFlags() & BDF_ORIENTATION_VERTICAL) {
rotationMode |= 1;
}
if (BurnDrvGetFlags() & BDF_ORIENTATION_FLIPPED) {
rotationMode ^= 2;
}
nVidImageWidth = gameWidth;
nVidImageHeight = gameHeight;
nVidImageDepth = 24;
nVidImageBPP = 3;
if (!rotationMode) {
nVidImagePitch = nVidImageWidth * nVidImageBPP;
} else {
nVidImagePitch = nVidImageHeight * nVidImageBPP;
}
SetBurnHighCol(nVidImageDepth);
bufferBytesPerPixel = nVidImageBPP;
bufferWidth = gameWidth;
bufferHeight = gameHeight;
int bufSize = bufferWidth * bufferHeight * nVidImageBPP;
free(screenBuffer);
screenBuffer = (unsigned char *) malloc(bufSize);
if (screenBuffer == NULL)
return 1;
nBurnBpp = nVidImageBPP;
nBurnPitch = nVidImagePitch;
pVidImage = screenBuffer;
memset(screenBuffer, 0, bufSize);
int textureWidth;
int textureHeight;
BOOL isRotated = rotationMode & 1;
if (!isRotated) {
textureWidth = bufferWidth;
textureHeight = bufferHeight;
} else {
textureWidth = bufferHeight;
textureHeight = bufferWidth;
}
NSSize screenSize = NSMakeSize((CGFloat)bufferWidth,
(CGFloat)bufferHeight);
[AppDelegate.sharedInstance.video notifyTextureReadyOfWidth:textureWidth
height:textureHeight
isRotated:isRotated
bytesPerPixel:bufferBytesPerPixel
screenSize:screenSize];
return 0;
}
static int MacOSVideoExit()
{
free(screenBuffer);
screenBuffer = NULL;
return 0;
}
static int MacOSVideoFrame(bool redraw)
{
if (pVidImage == NULL)
return NO;
if (redraw) {
if (BurnDrvRedraw()) {
BurnDrvFrame();
}
} else {
BurnDrvFrame();
}
return 0;
}
static int MacOSVideoPaint(int validate)
{
return [AppDelegate.sharedInstance.video renderToSurface:screenBuffer] ? 0 : 1;
}
static int MacOSVideoScale(RECT*, int, int)
{
return 0;
}
static int MacOSVideoGetSettings(InterfaceInfo *info)
{
return 0;
}
struct VidOut VidOutMacOS = {
MacOSVideoInit,
MacOSVideoExit,
MacOSVideoFrame,
MacOSVideoPaint,
MacOSVideoScale,
MacOSVideoGetSettings,
_T("MacOS Video"),
};

View File

@ -1,56 +0,0 @@
#include "burner.h"
static int Init()
{
return 0;
}
static int Exit()
{
return 0;
}
// Run one frame and render the screen
static int Frame(bool bRedraw)
{
if (bDrvOkay) {
if (bRedraw) { // Redraw current frame
if (BurnDrvRedraw()) {
BurnDrvFrame(); // No redraw function provided, advance one frame
}
} else {
BurnDrvFrame(); // Run one frame and draw the screen
}
if ((BurnDrvGetFlags() & BDF_16BIT_ONLY) && pVidTransCallback)
pVidTransCallback();
}
return 0;
}
// Paint the BlitFX surface onto the primary surface
static int Paint(int bValidate)
{
return 0;
}
static int Scale(RECT* , int, int)
{
return 0;
}
static int GetSettings(InterfaceInfo* pInfo)
{
return 0;
}
struct VidOut VidOutMacOS = {
Init,
Exit,
Frame,
Paint,
Scale,
GetSettings,
_T("MacOS Video"),
};