diff --git a/config/pine64,pinephone-1.0.ini b/config/pine64,pinephone-1.0.ini index 003c5e1..4d38caa 100644 --- a/config/pine64,pinephone-1.0.ini +++ b/config/pine64,pinephone-1.0.ini @@ -17,6 +17,8 @@ whitelevel=255 focallength=3.33 cropfactor=10.81 fnumber=3.0 +iso-min=100 +iso-max=64000 [front] driver=gc2145 diff --git a/config/pine64,pinephone-1.1.ini b/config/pine64,pinephone-1.1.ini index 003c5e1..4d38caa 100644 --- a/config/pine64,pinephone-1.1.ini +++ b/config/pine64,pinephone-1.1.ini @@ -17,6 +17,8 @@ whitelevel=255 focallength=3.33 cropfactor=10.81 fnumber=3.0 +iso-min=100 +iso-max=64000 [front] driver=gc2145 diff --git a/config/pine64,pinephone-1.2.ini b/config/pine64,pinephone-1.2.ini index 003c5e1..4d38caa 100644 --- a/config/pine64,pinephone-1.2.ini +++ b/config/pine64,pinephone-1.2.ini @@ -17,6 +17,8 @@ whitelevel=255 focallength=3.33 cropfactor=10.81 fnumber=3.0 +iso-min=100 +iso-max=64000 [front] driver=gc2145 diff --git a/config/pine64,pinetab.ini b/config/pine64,pinetab.ini index 003c5e1..4d38caa 100644 --- a/config/pine64,pinetab.ini +++ b/config/pine64,pinetab.ini @@ -17,6 +17,8 @@ whitelevel=255 focallength=3.33 cropfactor=10.81 fnumber=3.0 +iso-min=100 +iso-max=64000 [front] driver=gc2145 diff --git a/main.c b/main.c index 85d4b80..a8cbd3c 100644 --- a/main.c +++ b/main.c @@ -51,6 +51,11 @@ struct camerainfo { float focallength; float cropfactor; double fnumber; + int iso_min; + int iso_max; + + int gain_ctrl; + int gain_max; int has_af_c; int has_af_s; @@ -130,6 +135,23 @@ show_error(const char *s) gtk_widget_show(error_box); } +int +remap(int value, int input_min, int input_max, int output_min, int output_max) +{ + const long long factor = 1000000000; + long long output_spread = output_max - output_min; + long long input_spread = input_max - input_min; + + long long zero_value = value - input_min; + zero_value *= factor; + long long percentage = zero_value / input_spread; + + long long zero_output = percentage * output_spread / factor; + + long long result = output_min + zero_output; + return (int)result; +} + static void start_capturing(int fd) { @@ -255,6 +277,26 @@ v4l2_ctrl_get(int fd, uint32_t id) return ctrl.value; } +static int +v4l2_ctrl_get_max(int fd, uint32_t id) +{ + struct v4l2_queryctrl queryctrl; + int ret; + + memset(&queryctrl, 0, sizeof(queryctrl)); + + queryctrl.id = id; + ret = xioctl(fd, VIDIOC_QUERYCTRL, &queryctrl); + if (ret) + return 0; + + if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) { + return 0; + } + + return queryctrl.maximum; +} + static int v4l2_has_control(int fd, int control_id) { @@ -280,6 +322,7 @@ draw_controls() { cairo_t *cr; char iso[6]; + int temp; char shutterangle[6]; if (auto_exposure) { @@ -291,7 +334,8 @@ draw_controls() if (auto_gain) { sprintf(iso, "auto"); } else { - sprintf(iso, "%d", gain); + temp = remap(gain - 1, 0, current.gain_max, current.iso_min, current.iso_max); + sprintf(iso, "%d", temp); } if (status_surface) @@ -396,6 +440,16 @@ init_sensor(char *fn, int width, int height, int mbus, int rate) current.has_af_s = 1; } + if (v4l2_has_control(fd, V4L2_CID_GAIN)) { + current.gain_ctrl = V4L2_CID_GAIN; + current.gain_max = v4l2_ctrl_get_max(fd, V4L2_CID_GAIN); + } + + if (v4l2_has_control(fd, V4L2_CID_ANALOGUE_GAIN)) { + current.gain_ctrl = V4L2_CID_ANALOGUE_GAIN; + current.gain_max = v4l2_ctrl_get_max(fd, V4L2_CID_ANALOGUE_GAIN); + } + auto_exposure = 1; auto_gain = 1; draw_controls(); @@ -541,6 +595,7 @@ process_image(const int *p, int size) uint64 exif_offset = 0; static const short cfapatterndim[] = {2, 2}; static const float neutral[] = {1.0, 1.0, 1.0}; + static uint16_t isospeed[] = {0}; // Only process preview frames when not capturing if (capture == 0) { @@ -591,6 +646,10 @@ process_image(const int *p, int size) sprintf(fname_target, "%s/Pictures/IMG%s", getenv("HOME"), timestamp); sprintf(fname, "%s/%d.dng", burst_dir, burst_length - capture); + // Get latest exposure and gain now the auto gain/exposure is disabled while capturing + gain = v4l2_ctrl_get(current.fd, current.gain_ctrl); + exposure = v4l2_ctrl_get(current.fd, V4L2_CID_EXPOSURE); + if(!(tif = TIFFOpen(fname, "w"))) { printf("Could not open tiff\n"); } @@ -634,7 +693,6 @@ process_image(const int *p, int size) } TIFFWriteDirectory(tif); - // Define main photo TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 0); TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, current.width); @@ -664,7 +722,17 @@ process_image(const int *p, int size) // Add an EXIF block to the tiff TIFFCreateEXIFDirectory(tif); // 1 = manual, 2 = full auto, 3 = aperture priority, 4 = shutter priority - TIFFSetField(tif, EXIFTAG_EXPOSUREPROGRAM, 2); + if (auto_exposure) { + TIFFSetField(tif, EXIFTAG_EXPOSUREPROGRAM, 2); + } else { + TIFFSetField(tif, EXIFTAG_EXPOSUREPROGRAM, 1); + } + + TIFFSetField(tif, EXIFTAG_EXPOSURETIME, (1.0/current.rate) / ((float)current.height / (float)exposure)); + isospeed[0] = (uint16_t)remap(gain - 1, 0, current.gain_max, current.iso_min, current.iso_max); + TIFFSetField(tif, EXIFTAG_ISOSPEEDRATINGS, 1, isospeed); + TIFFSetField(tif, EXIFTAG_FLASH, 0); + TIFFSetField(tif, EXIFTAG_DATETIMEORIGINAL, datetime); TIFFSetField(tif, EXIFTAG_DATETIMEDIGITIZED, datetime); if(current.fnumber) { @@ -718,6 +786,14 @@ process_image(const int *p, int size) sprintf(command, "%s %s %s &", processing_script, burst_dir, fname_target); system(command); + // Restore the auto exposure and gain if needed + if (auto_exposure) { + v4l2_ctrl_set(current.fd, V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_AUTO); + } + if (auto_gain) { + v4l2_ctrl_set(current.fd, V4L2_CID_AUTOGAIN, 1); + } + } } } @@ -902,6 +978,10 @@ config_ini_handler(void *user, const char *section, const char *name, cc->cropfactor = strtof(value, NULL); } else if (strcmp(name, "fnumber") == 0) { cc->fnumber = strtod(value, NULL); + } else if (strcmp(name, "iso-min") == 0) { + cc->iso_min = strtod(value, NULL); + } else if (strcmp(name, "iso-max") == 0) { + cc->iso_max = strtod(value, NULL); } else { g_printerr("Unknown key '%s' in [%s]\n", name, section); exit(1); @@ -1118,6 +1198,10 @@ on_shutter_clicked(GtkWidget *widget, gpointer user_data) } strcpy(burst_dir, tempdir); + + // Disable the autogain/exposure while taking the burst + v4l2_ctrl_set(current.fd, V4L2_CID_AUTOGAIN, 0); + v4l2_ctrl_set(current.fd, V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL); capture = burst_length; } @@ -1142,8 +1226,8 @@ on_preview_tap(GtkWidget *widget, GdkEventButton *event, gpointer user_data) current_control = USER_CONTROL_ISO; gtk_label_set_text(GTK_LABEL(control_name), "ISO"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(control_auto), auto_gain); - gtk_adjustment_set_lower(GTK_ADJUSTMENT(control_slider), 1.0); - gtk_adjustment_set_upper(GTK_ADJUSTMENT(control_slider), 1024.0); + gtk_adjustment_set_lower(GTK_ADJUSTMENT(control_slider), 0.0); + gtk_adjustment_set_upper(GTK_ADJUSTMENT(control_slider), (float)current.gain_max); gtk_adjustment_set_value(GTK_ADJUSTMENT(control_slider), (double)gain); } else if (event->x > 60 && event->x < 120) { @@ -1243,7 +1327,7 @@ on_control_slider_changed(GtkAdjustment *widget, gpointer user_data) switch (current_control) { case USER_CONTROL_ISO: gain = (int)value; - v4l2_ctrl_set(current.fd, V4L2_CID_GAIN, gain); + v4l2_ctrl_set(current.fd, current.gain_ctrl, gain); break; case USER_CONTROL_SHUTTER: exposure = (int)(value / 360.0 * current.height);