Skip to content

Commit

Permalink
Support camera preview
Browse files Browse the repository at this point in the history
  • Loading branch information
yushulx committed Dec 24, 2024
1 parent e0a9cad commit f096c98
Show file tree
Hide file tree
Showing 2 changed files with 233 additions and 2 deletions.
5 changes: 3 additions & 2 deletions litecam/include/CameraPreview.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
#elif __linux__
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#elif __APPLE__
#include <Cocoa/Cocoa.h>
// #elif __APPLE__
// #include <Cocoa/Cocoa.h>
#endif

// Export macro for shared library
Expand Down Expand Up @@ -66,6 +66,7 @@ class CAMERA_API CameraWindow
#elif __APPLE__
// Add macOS-specific members (e.g., NSWindow or CGContext)
void *nsWindow; // Use proper macOS data structures here
void *contentView;
#endif
};

Expand Down
230 changes: 230 additions & 0 deletions litecam/src/CameraPreviewMacOS.mm
Original file line number Diff line number Diff line change
@@ -1 +1,231 @@
/*
CameraPreviewMacOS.mm
*/

#include "CameraPreview.h"
#import <Cocoa/Cocoa.h>
#include <vector>
#include <string>
#include <utility>

// Define a C++ implementation struct to hold drawing data
struct CameraContentViewImpl {
std::vector<unsigned char> rgbData;
int frameWidth = 0;
int frameHeight = 0;
std::vector<std::pair<int, int>> contourPoints;
std::string displayText;
CameraWindow::Color textColor;
};

// Objective-C subclass of NSView to handle custom drawing
@interface CameraContentView : NSView
{
CameraContentViewImpl* impl; // Pointer to C++ implementation
}
- (void)updateFrame:(const unsigned char*)data width:(int)width height:(int)height;
- (void)updateContour:(const std::vector<std::pair<int, int>>&)points;
- (void)updateText:(const std::string&)text
x:(int)x
y:(int)y
fontSize:(int)fontSize
color:(const CameraWindow::Color&)color;
@end

@implementation CameraContentView

- (instancetype)initWithFrame:(NSRect)frameRect {
self = [super initWithFrame:frameRect];
if (self) {
impl = new CameraContentViewImpl();
}
return self;
}

- (void)dealloc {
delete impl;
[super dealloc];
}

- (void)updateFrame:(const unsigned char*)data width:(int)width height:(int)height {
impl->rgbData.assign(data, data + (width * height * 3));
impl->frameWidth = width;
impl->frameHeight = height;
[self setNeedsDisplay:YES];
}

- (void)updateContour:(const std::vector<std::pair<int, int>>&)points {
impl->contourPoints = points;
[self setNeedsDisplay:YES];
}

- (void)updateText:(const std::string&)text
x:(int)x
y:(int)y
fontSize:(int)fontSize
color:(const CameraWindow::Color&)color {
impl->displayText = text;
impl->textColor = color;
[self setNeedsDisplay:YES];
}

- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];

CGContextRef context = [[NSGraphicsContext currentContext] CGContext];
if (impl->rgbData.empty() || impl->frameWidth == 0 || impl->frameHeight == 0) {
return;
}

// Draw the RGB frame
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, impl->rgbData.data(), impl->rgbData.size(), NULL);
CGImageRef image = CGImageCreate(impl->frameWidth, impl->frameHeight, 8, 24, impl->frameWidth * 3, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaNone, provider, NULL, false, kCGRenderingIntentDefault);

CGRect rect = CGRectMake(0, 0, impl->frameWidth, impl->frameHeight);
CGContextDrawImage(context, rect, image);

CGImageRelease(image);
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorSpace);

// Draw Contour
if (!impl->contourPoints.empty()) {
CGContextSetLineWidth(context, 2.0);
CGContextSetStrokeColorWithColor(context, [[NSColor greenColor] CGColor]);

CGContextMoveToPoint(context, impl->contourPoints[0].first, impl->frameHeight - impl->contourPoints[0].second);
for (size_t i = 1; i < impl->contourPoints.size(); ++i) {
CGContextAddLineToPoint(context, impl->contourPoints[i].first, impl->frameHeight - impl->contourPoints[i].second);
}
CGContextClosePath(context);
CGContextStrokePath(context);
}

// Draw Text
if (!impl->displayText.empty()) {
NSDictionary *attributes = @{
NSFontAttributeName : [NSFont systemFontOfSize:24],
NSForegroundColorAttributeName : [NSColor colorWithCalibratedRed:(impl->textColor.r / 255.0)
green:(impl->textColor.g / 255.0)
blue:(impl->textColor.b / 255.0)
alpha:1.0]
};
NSPoint point = NSMakePoint(50, impl->frameHeight - 50);
NSString *nsText = [NSString stringWithUTF8String:impl->displayText.c_str()];
[nsText drawAtPoint:point withAttributes:attributes];
}
}

@end

// Delegate to handle window events
@interface CameraWindowDelegate : NSObject <NSWindowDelegate>
@end

@implementation CameraWindowDelegate
- (BOOL)windowShouldClose:(id)sender {
[NSApp terminate:nil];
return YES;
}
@end

// Implementation of CameraWindow class
CameraWindow::CameraWindow(int width, int height, const std::string &title)
: width(width), height(height), title(title), nsWindow(nullptr), contentView(nullptr) {}

CameraWindow::~CameraWindow() {
if (nsWindow) {
NSWindow *window = (__bridge NSWindow *)nsWindow;
[window close];
}
}

bool CameraWindow::Create() {
@autoreleasepool {
// Initialize the application if not already running
if (NSApp == nil) {
[NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
[NSApp finishLaunching];
}

// Create the window
NSRect frame = NSMakeRect(100, 100, width, height);
NSUInteger styleMask = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable;
NSWindow *window = [[NSWindow alloc] initWithContentRect:frame
styleMask:styleMask
backing:NSBackingStoreBuffered
defer:NO];
if (!window) {
return false;
}

[window setTitle:[NSString stringWithUTF8String:title.c_str()]];
[window makeKeyAndOrderFront:nil];

// Initialize the custom content view
CameraContentView *cv = [[CameraContentView alloc] initWithFrame:frame];
[window setContentView:cv];
contentView = cv;

// Set the delegate to handle window events
CameraWindowDelegate *delegate = [[CameraWindowDelegate alloc] init];
[window setDelegate:delegate];

nsWindow = (void *)window;
return true;
}
}

void CameraWindow::Show() {
@autoreleasepool {
[NSApp activateIgnoringOtherApps:YES];
}
}

bool CameraWindow::WaitKey(char key)
{
@autoreleasepool {
// Poll for *any* event quickly, instead of blocking forever
NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny
untilDate:[NSDate distantPast]
inMode:NSDefaultRunLoopMode
dequeue:YES];
if (event) {
// Dispatch the event so the window can update, move, resize, etc.
[NSApp sendEvent:event];

// Check if it’s a keydown event
if (event.type == NSEventTypeKeyDown) {
NSString *characters = [event charactersIgnoringModifiers];
if ([characters length] > 0) {
char pressedKey = [characters characterAtIndex:0];
if (key == '\0' || pressedKey == key || pressedKey == std::toupper(key)) {
return false; // Return false to break your while-loop
}
}
}
}
// If no event was found, just continue
return true;
}
}

void CameraWindow::ShowFrame(const unsigned char *rgbData, int frameWidth, int frameHeight) {
if (contentView) {
[contentView updateFrame:rgbData width:frameWidth height:frameHeight];
}
}

void CameraWindow::DrawContour(const std::vector<std::pair<int, int>> &points) {
if (contentView) {
[contentView updateContour:points];
}
}

void CameraWindow::DrawText(const std::string &text, int x, int y, int fontSize, const Color &color) {
if (contentView) {
[contentView updateText:text x:x y:y fontSize:fontSize color:color];
}
}

0 comments on commit f096c98

Please sign in to comment.