diff --git a/platform/cocoa/Makefile b/platform/cocoa/Makefile index f7945792e..da05b6eca 100644 --- a/platform/cocoa/Makefile +++ b/platform/cocoa/Makefile @@ -4,7 +4,7 @@ appname = "MLV App" # List of all objects to link objects = main.o video_mlv.o debayer.o amaze_demosaic.o raw_processing.o \ main_methods.o useful_methods.o background_thread.o matrix.o \ - camera_matrices.o frame_caching.o lj92.o + camera_matrices.o frame_caching.o lj92.o session_methods.o # Compiler CC = gcc # Normal @@ -54,6 +54,9 @@ main.o : main.m main_methods.o : main_methods.m $(CC) $(cflags) main_methods.m +session_methods.o : session_methods.m + $(CC) $(cflags) session_methods.m + background_thread.o : background_thread.m $(CC) $(cflags) background_thread.m diff --git a/platform/cocoa/godobject.h b/platform/cocoa/godobject.h index 709a2c5fa..0c50cffe9 100644 --- a/platform/cocoa/godobject.h +++ b/platform/cocoa/godobject.h @@ -65,16 +65,23 @@ typedef struct { NSButton * alwaysUseAmazeSelector; NSButton * tonemappingSelector; + /* Select image profile */ + NSPopUpButton * imageProfile; + /**************************************** ** SESSION STUFF ** ****************************************/ - /* Number of clips */ - int clipCount; - /* List of clips loaded (in session) */ - NSTableView * clipTable; - /* Info about each one (array as long as clipcount) */ - clipInfo_t * clipInfo; + struct { + + /* Number of clips */ + int clipCount; + /* List of clips loaded (in session) */ + NSTableView * clipTable; + /* Info about each one (array as long as clipcount) */ + clipInfo_t * clipInfo; + + } session; /**************************************** ** END OF SESSION STUFF ** diff --git a/platform/cocoa/main.m b/platform/cocoa/main.m index ff1379f08..45b100d08 100644 --- a/platform/cocoa/main.m +++ b/platform/cocoa/main.m @@ -36,6 +36,10 @@ godObject_t * App; +/* Default image profile when app is loaded */ +#define DEFAULT_IMAGE_PROFILE_APP PROFILE_TONEMAPPED + + int main(int argc, char * argv[]) { /* Apple documentation says this is right way to do things */ @@ -97,15 +101,18 @@ int NSApplicationMain(int argc, const char * argv[]) App->window.titlebarAppearsTransparent = true; /* Processing style selector */ - NSPopUpButton * processingStyle = [ - [NSPopUpButton alloc] - initWithFrame: NSMakeRect( RIGHT_SIDEBAR_SLIDER(0,24,19) ) - ]; - [processingStyle anchorRight: YES]; - [processingStyle anchorTop: YES]; - [processingStyle addItemWithTitle: @"Faithful"]; - // [processingStyle addItemWithTitle: @"Milo"]; - [[App->window contentView] addSubview: processingStyle]; + App->imageProfile = [ [NSPopUpButton alloc] + initWithFrame: NSMakeRect( RIGHT_SIDEBAR_SLIDER(0,24,19) ) ]; + [App->imageProfile anchorRight: YES]; + [App->imageProfile anchorTop: YES]; + [App->imageProfile insertItemWithTitle: @"Standard" atIndex: PROFILE_STANDARD]; + [App->imageProfile insertItemWithTitle: @"Tonemapped" atIndex: PROFILE_TONEMAPPED]; + [App->imageProfile insertItemWithTitle: @"Alexa Log" atIndex: PROFILE_ALEXA_LOG]; + [App->imageProfile insertItemWithTitle: @"Linear" atIndex: PROFILE_LINEAR]; + [App->imageProfile setTarget: App->imageProfile]; + [App->imageProfile setAction: @selector(toggleImageProfile)]; + [App->imageProfile selectItemAtIndex: DEFAULT_IMAGE_PROFILE_APP]; + [[App->window contentView] addSubview: App->imageProfile]; /* Yes, macros - Az u can tell by the capietals. * I don't want to add hundreds of lines of Objective C @@ -135,7 +142,7 @@ int NSApplicationMain(int argc, const char * argv[]) /* Maybe we won't have sharpness */ /* Enable/disable highlight reconstruction */ - App->highlightReconstructionSelector = [ [NSButton alloc] + App->highlightReconstructionSelector = [ [NSButton alloc] initWithFrame: NSMakeRect( RIGHT_SIDEBAR_SLIDER(11, ELEMENT_HEIGHT, 16) )]; [App->highlightReconstructionSelector setButtonType: NSSwitchButton]; [App->highlightReconstructionSelector setTitle: @"Highlight Reconstruction"]; @@ -146,7 +153,7 @@ int NSApplicationMain(int argc, const char * argv[]) [[App->window contentView] addSubview: App->highlightReconstructionSelector]; /* To set always use AMaZE on/off */ - App->alwaysUseAmazeSelector = [ [NSButton alloc] + App->alwaysUseAmazeSelector = [ [NSButton alloc] initWithFrame: NSMakeRect( RIGHT_SIDEBAR_SLIDER(12, ELEMENT_HEIGHT, 30) )]; [App->alwaysUseAmazeSelector setButtonType: NSSwitchButton]; [App->alwaysUseAmazeSelector setTitle: @"Always use AMaZE"]; @@ -156,17 +163,6 @@ int NSApplicationMain(int argc, const char * argv[]) [App->alwaysUseAmazeSelector setAction: @selector(toggleAlwaysAmaze)]; [[App->window contentView] addSubview: App->alwaysUseAmazeSelector]; - /* To set enable/disable tonemapping */ - App->tonemappingSelector = [ [NSButton alloc] - initWithFrame: NSMakeRect( RIGHT_SIDEBAR_SLIDER(13, ELEMENT_HEIGHT, 44) )]; - [App->tonemappingSelector setButtonType: NSSwitchButton]; - [App->tonemappingSelector setTitle: @"Apply Tonemapping"]; - [App->tonemappingSelector anchorRight: YES]; - [App->tonemappingSelector anchorTop: YES]; - [App->tonemappingSelector setTarget: App->tonemappingSelector]; - [App->tonemappingSelector setAction: @selector(toggleTonemapping)]; - [[App->window contentView] addSubview: App->tonemappingSelector]; - /* ******************************************************************************* * LEFT SIDEBAR STUFF @@ -175,12 +171,8 @@ int NSApplicationMain(int argc, const char * argv[]) /* Open MLV file button */ CREATE_BUTTON_LEFT_TOP( App->openMLVButton, 0, openMlvDialog, 0, @"Open MLV File" ); - /* Export an image sequence (temporary) - these buttons look awkward and awful :[ */ - // CREATE_BUTTON_LEFT_BOTTOM( exportJpegSequenceButton, 1, exportJpegSequence, 1, @"Export JPEG Sequence" ); - // CREATE_BUTTON_LEFT_BOTTOM( exportPngSequenceButton, 0, exportPngSequence, 1, @"Export PNG Sequence" ); + // CREATE_BUTTON_LEFT_TOP( App->openMLVButton, 1, openMlvDialog, 6, @"Open Session" ); /* Commented out as not working yet */ CREATE_BUTTON_LEFT_BOTTOM( App->exportProRes4444Button, 0, exportProRes4444, 1, @"Export ProRes 4444" ); - /* Black level user input/adjustment */ - // CREATE_INPUT_WITH_LABEL_LEFT( blackLevelEntry, 1, blackLevelSet, 0, @"Black Level:" ); /* NSTableView - List of all clips currently open (session) */ // NSScrollView * tableContainer = [[NSScrollView alloc] initWithFrame:NSMakeRect(10, 10, 380, 200)]; @@ -206,6 +198,8 @@ int NSApplicationMain(int argc, const char * argv[]) /* Set exposure to + 1.2 stops instead of correct 0.0, this is to give the impression * (to those that believe) that highlights are recoverable (shhh don't tell) */ processingSetExposureStops(App->processingSettings, 1.2); + /* TEST */ + processingSetImageProfile(App->processingSettings, DEFAULT_IMAGE_PROFILE_APP); /* Link video with processing settings */ setMlvProcessing(App->videoMLV, App->processingSettings); /* Limit frame cache to suitable amount of RAM (~33% at 8GB and below, ~50% at 16GB, then up and up) */ diff --git a/platform/cocoa/main_methods.h b/platform/cocoa/main_methods.h index 2f3bac8ae..323a980a9 100644 --- a/platform/cocoa/main_methods.h +++ b/platform/cocoa/main_methods.h @@ -1,5 +1,5 @@ -#ifndef _main_methods_ -#define _main_methods_ +#ifndef _main_methods_h_ +#define _main_methods_h_ /* Methods that actually do stuff related to MLV on user interface interactions */ @@ -25,6 +25,14 @@ void setAppNewMlvClip(char * mlvPathString, char * mlvFileName); @end +/* NSPopUpButton methods */ +@interface NSPopUpButton (mainMethods) + +/* Select processing image profile */ +-(void)toggleImageProfile; + +@end + /* Slider methods */ @interface NSSlider (mainMethods) diff --git a/platform/cocoa/main_methods.m b/platform/cocoa/main_methods.m index 756abaf00..4846ec44c 100644 --- a/platform/cocoa/main_methods.m +++ b/platform/cocoa/main_methods.m @@ -272,8 +272,22 @@ -(void)exportProRes4444 @end -/* Slider methods */ +/* NSPopUpButton methods */ +@implementation NSPopUpButton (mainMethods) + +/* Select processing image profile */ +-(void)toggleImageProfile +{ + /* Indexes of menu items correspond to defines of processing profiles */ + processingSetImageProfile(App->processingSettings, (int)[self indexOfSelectedItem]); + App->frameChanged++; +} + +@end + + +/* Slider methods */ @implementation NSSlider (mainMethods) /* Change frame based on time slider */ diff --git a/platform/cocoa/session_methods.h b/platform/cocoa/session_methods.h new file mode 100644 index 000000000..bc768027e --- /dev/null +++ b/platform/cocoa/session_methods.h @@ -0,0 +1,29 @@ +#ifndef _session_methods_h_ +#define _session_methods_h_ + +/* Methods/functions for handling sessions */ + + +/* This is a function as it may be used in more than one place */ +void sessionAddNewMlvClip(char * mlvPathString, char * mlvFileName); +/* Called from -(void)openSessionDialog - currently only loads first clip */ +void appLoadSession(char * sessionPath); +/* Frees/deletes all mlv objects */ +void appClearSession(); + + +/* Button methods */ +@interface NSButton (sessionMethods) + +/* Opens a dialog to select MLV file + sets MLV file to that */ +-(void)openSessionDialog; + +@end + + +/* Slider methods */ +@interface NSSlider (sessionMethods) + +@end + +#endif diff --git a/platform/cocoa/session_methods.m b/platform/cocoa/session_methods.m new file mode 100644 index 000000000..513d6bfd9 --- /dev/null +++ b/platform/cocoa/session_methods.m @@ -0,0 +1,84 @@ +/* Methods for user interface interactions + * this is where some real code goes */ + +#include +#include +#include + +#import "Cocoa/Cocoa.h" + +#include "gui_stuff/app_design.h" + +#include "main_methods.h" +#include "../../src/mlv_include.h" + +#include "mac_info.h" + +#include "background_thread.h" + +/* God object used to share globals (type) */ +#include "godobject.h" +/* The godobject itsself */ +extern godObject_t * App; + + +/* Methods/functions for handling sessions */ + +/* This is a function as it may be used in more than one place */ +void sessionAddNewMlvClip(char * mlvPathString, char * mlvFileName) +{ + return; + /* Do clip adding stuff here... */ + App->session.clipCount++; +} + +/* Called from -(void)openSessionDialog - currently only loads first clip */ +void appLoadSession(char * sessionPath) +{ + /* Open the MASXML file 4 reading */ + FILE * session_file = fopen(sessionPath, "rb"); + + /* Get size of file */ + fseek(session_file, 0, SEEK_END); + uint64_t masxml_size = ftell(session_file); + + /* Don't allow files over over 8MB */ + if (masxml_size > (1 << 23)) return; + + /* Read whole session in to memory */ + char * session_xml = calloc(masxml_size, sizeof(char)); + fread(session_xml, sizeof(char), masxml_size, session_file); + fclose(session_file); + + /* Parse the XML... */ + + /* This will be boring to write :( */ + + free(session_xml); +} + +/* Frees/deletes all mlv objects */ +void appClearSession() +{ + /* Not done as you can see */ + App->session.clipCount = 0; + return; +} + + +/* Button methods */ +@implementation NSButton (sessionMethods) + +/* Opens a dialog to select MLV file + sets MLV file to that */ +-(void)openSessionDialog +{ + return; +} + +@end + + +/* Slider methods */ +@implementation NSSlider (sessionMethods) + +@end \ No newline at end of file diff --git a/src/processing/processing_object.h b/src/processing/processing_object.h index 2fefd657a..459fc6759 100644 --- a/src/processing/processing_object.h +++ b/src/processing/processing_object.h @@ -4,6 +4,13 @@ /* Processing settings structure (a mess) */ typedef struct { + /* Image profile, options: + * STANDARD - Gamma Corrected + * TONEMAPPED - Gamma Corrected + Tonemapped + * ALEXA_LOG - Alexa log (Also known as Log-C) + * LINEAR - Linear, idk who would want this */ + int image_profile; + /* (RAW) white and black levels */ int black_level, white_level; @@ -61,11 +68,12 @@ typedef struct { * will be calculated on setting changes, values 0-65535 */ uint16_t * pre_calc_curve_r; uint16_t * pre_calc_curve_g; - uint16_t * pre_calc_curve_b; + uint16_t * pre_calc_curve_b; int use_rgb_curves; /* The r, g and b curves can be disabled */ uint16_t * pre_calc_levels; /* For black level and white level */ uint16_t * pre_calc_gamma; + uint16_t * pre_calc_o_curve; int use_o_curve; /* Output curve - not always used */ /* Precalculated values for saturation */ - int32_t * pre_calc_sat; + int32_t * pre_calc_sat; int use_saturation; /* Saturation is disable-able */ } processingObject_t; diff --git a/src/processing/raw_processing.c b/src/processing/raw_processing.c index 3ffbaddbd..c84ae4621 100644 --- a/src/processing/raw_processing.c +++ b/src/processing/raw_processing.c @@ -10,6 +10,8 @@ /* Matrix functions which are useful */ #include "../matrix/matrix.h" +#define STANDARD_GAMMA 3.15 + #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) #define MAX(X, Y) (((X) > (Y)) ? (X) : (Y)) #define LIMIT16(X) MAX(MIN(X, 65535), 0) @@ -27,6 +29,7 @@ processingObject_t * initProcessingObject() processing->pre_calc_curve_b = malloc( 65536 * sizeof(uint16_t) ); processing->pre_calc_gamma = malloc( 65536 * sizeof(uint16_t) ); processing->pre_calc_levels = malloc( 65536 * sizeof(uint16_t) ); + processing->pre_calc_o_curve = malloc( 65535 * sizeof(uint16_t) ); processing->pre_calc_sat = malloc( 131072 * sizeof(int32_t) ); /* For precalculated matrix values */ @@ -46,9 +49,10 @@ processingObject_t * initProcessingObject() processingSetWhiteBalance(processing, 6250.0, 0.0); processingSetBlackAndWhiteLevel(processing, 8192.0, 64000.0); /* 16 bit! */ processingSetExposureStops(processing, 0.0); - processingSetGamma(processing, 3.15); + processingSetGamma(processing, STANDARD_GAMMA); processingSetSaturation(processing, 1.0); processingSetContrast(processing, 0.73, 5.175, 0.5, 0.0, 0.0); + processingSetImageProfile(processing, PROFILE_TONEMAPPED); /* Just in case (should be done tho already) */ processing_update_matrices(processing); @@ -56,6 +60,56 @@ processingObject_t * initProcessingObject() return processing; } +void processingSetImageProfile(processingObject_t * processing, int imageProfile) +{ + processing->image_profile = imageProfile; + + if (imageProfile == PROFILE_STANDARD) + { + /* output curve is not used in this case */ + processing->use_o_curve = 0; + processing->use_rgb_curves = 1; + processing->use_saturation = 1; + processingSetGamma(processing, STANDARD_GAMMA); + processing_disable_tonemapping(processing); + } + else if (imageProfile == PROFILE_TONEMAPPED) + { + processing->use_o_curve = 0; + processing->use_rgb_curves = 1; + processing->use_saturation = 1; + processingSetGamma(processing, STANDARD_GAMMA); + processing_enable_tonemapping(processing); + } + /* Alexa Log info from: http://www.vocas.nl/webfm_send/964 */ + else if (imageProfile == PROFILE_ALEXA_LOG) + { + processing->use_o_curve = 1; + processing->use_rgb_curves = 0; + processing->use_saturation = 0; + /* Calculate Alexa Log curve (iso 800 version) */ + for (int i = 0; i < 65536; ++i) + { + double value = (double)i / 65535.0; + value = (value > 0.010591) ? (0.247190 * log10(5.555556 * value + 0.052272) + 0.385537) : (5.367655 * value + 0.092809); + value *= 65535.0; + processing->pre_calc_o_curve[i] = (uint16_t)value; + } + /* We won't even need gamma here */ + processingSetGamma(processing, 1.0); + processing_disable_tonemapping(processing); + } + else if (imageProfile == PROFILE_LINEAR) + { + processing->use_o_curve = 0; + processing->use_rgb_curves = 1; + processing->use_saturation = 1; + processingSetGamma(processing, 1.0); + processing_disable_tonemapping(processing); + } + else return; +} + /* Takes those matrices I learned about on the forum */ void processingSetXyzToCamMatrix(processingObject_t * processing, double * xyzToCamMatrix) @@ -133,30 +187,44 @@ void applyProcessingObject( processingObject_t * processing, } } - /* Now saturation (looks way better after gamma) - * Also this algorithm is slow, but temporary */ - for (uint16_t * pix = outputImage; pix < img_end; pix += 3) + if (processing->use_saturation) { - /* Pixel brightness = 4/16 R, 11/16 G, 1/16 blue; Try swapping the channels, it will look worse */ - int32_t Y1 = ((pix[0] << 2) + (pix[1] * 11) + pix[2]) >> 4; - int32_t Y2 = Y1 - 65536; - - /* Increase difference between channels and the saturation midpoint */ - int32_t pix0 = processing->pre_calc_sat[pix[0] - Y2] + Y1; - int32_t pix1 = processing->pre_calc_sat[pix[1] - Y2] + Y1; - int32_t pix2 = processing->pre_calc_sat[pix[2] - Y2] + Y1; + /* Now saturation (looks way better after gamma) */ + for (uint16_t * pix = outputImage; pix < img_end; pix += 3) + { + /* Pixel brightness = 4/16 R, 11/16 G, 1/16 blue; Try swapping the channels, it will look worse */ + int32_t Y1 = ((pix[0] << 2) + (pix[1] * 11) + pix[2]) >> 4; + int32_t Y2 = Y1 - 65536; + + /* Increase difference between channels and the saturation midpoint */ + int32_t pix0 = processing->pre_calc_sat[pix[0] - Y2] + Y1; + int32_t pix1 = processing->pre_calc_sat[pix[1] - Y2] + Y1; + int32_t pix2 = processing->pre_calc_sat[pix[2] - Y2] + Y1; + + pix[0] = LIMIT16(pix0); + pix[1] = LIMIT16(pix1); + pix[2] = LIMIT16(pix2); + } + } - pix[0] = LIMIT16(pix0); - pix[1] = LIMIT16(pix1); - pix[2] = LIMIT16(pix2); + if (processing->use_rgb_curves) + { + /* Contrast Curve (OMG putting this after gamma made it 999x better) */ + for (uint16_t * pix = outputImage; pix < img_end; pix += 3) + { + pix[0] = processing->pre_calc_curve_r[ pix[0] ]; + pix[1] = processing->pre_calc_curve_r[ pix[1] ]; + pix[2] = processing->pre_calc_curve_r[ pix[2] ]; + } } - /* Contrast Curve (OMG putting this after gamma made it 999x better) */ - for (uint16_t * pix = outputImage; pix < img_end; pix += 3) + /* Ouput curve (if needed) */ + if (processing->use_o_curve) { - pix[0] = processing->pre_calc_curve_r[ pix[0] ]; - pix[1] = processing->pre_calc_curve_r[ pix[1] ]; - pix[2] = processing->pre_calc_curve_r[ pix[2] ]; + for (int i = 0; i < img_s; ++i) + { + outputImage[i] = processing->pre_calc_o_curve[ outputImage[i] ]; + } } } @@ -355,7 +423,7 @@ void processingSet3WayCorrection( processingObject_t * processing, processing_update_curves(processing); } -void processingEnableTonemapping(processingObject_t * processing) +void processing_enable_tonemapping(processingObject_t * processing) { (processing)->tone_mapping = 1; /* This will update everything necessary to enable tonemapping */ @@ -363,7 +431,7 @@ void processingEnableTonemapping(processingObject_t * processing) processing_update_matrices(processing); } -void processingDisableTonemapping(processingObject_t * processing) +void processing_disable_tonemapping(processingObject_t * processing) { (processing)->tone_mapping = 0; /* This will update everything necessary to disable tonemapping */ @@ -423,6 +491,7 @@ void freeProcessingObject(processingObject_t * processing) free(processing->pre_calc_curve_b); free(processing->pre_calc_gamma); free(processing->pre_calc_levels); + free(processing->pre_calc_o_curve); free(processing->pre_calc_sat); for (int i = 0; i < 9; ++i) free(processing->pre_calc_matrix[i]); free(processing); diff --git a/src/processing/raw_processing.h b/src/processing/raw_processing.h index 01d20db25..e7ecd4f3b 100644 --- a/src/processing/raw_processing.h +++ b/src/processing/raw_processing.h @@ -13,6 +13,16 @@ void freeProcessingObject(processingObject_t * processing); +/* Set image profile possible TODO: make image profile a type, so custom ones can be developed */ +void processingSetImageProfile(processingObject_t * processing, int imageProfile); +/* imageProfile argument options: */ +#define PROFILE_STANDARD 0 /* Gamma Corrected */ +#define PROFILE_TONEMAPPED 1 /* Gamma Corrected + Tonemapped */ +#define PROFILE_ALEXA_LOG 2 /* Alexa log (Also known as Log-C) */ +#define PROFILE_LINEAR 3 /* Linear, idk who would want this */ + + + /* Process a RAW frame with settings from a processing object * - image must be debayered and RGB plz + thx! */ void applyProcessingObject( processingObject_t * processing, @@ -105,9 +115,9 @@ void processingSetSaturation(processingObject_t * processing, double saturationF -/* Enable/disable tonemapping */ -void processingEnableTonemapping(processingObject_t * processing); -void processingDisableTonemapping(processingObject_t * processing); +/* Enable/disable tonemapping - DEPRECATED!!! (made private) */ +#define processingEnableTonemapping(processing) processing_enable_tonemapping(processing) +#define processingDisableTonemapping(processing) processing_disable_tonemapping(processing) @@ -130,6 +140,10 @@ double add_contrast( double pixel, double light_contrast_range, double light_contrast_factor ); +/* Enable/disable tonemapping - DEPRECATED!!! */ +void processing_enable_tonemapping(processingObject_t * processing); +void processing_disable_tonemapping(processingObject_t * processing); + /* Returns multipliers for white balance by (linearly) interpolating measured * Canon values... stupidly simple, also range limited to 2500-10000 */ void get_kelvin_multipliers_rgb(double kelvin, double * multiplier_output);