12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061 |
- #include "process_pipeline.h"
- #include "gles2_debayer.h"
- #include "io_pipeline.h"
- #include "main.h"
- #include "pipeline.h"
- #include "state.h"
- #include "zbar_pipeline.h"
- #include <assert.h>
- #include <gtk/gtk.h>
- #include <math.h>
- #ifndef SYSCONFDIR
- #include "config.h"
- #endif
- #include "dcp.h"
- #include "gl_util.h"
- #include "libdng.h"
- #include <jpeglib.h>
- #include <sys/mman.h>
- #include <sys/prctl.h>
- static const float colormatrix_srgb[] = { 3.2409f, -1.5373f, -0.4986f,
- -0.9692f, 1.8759f, 0.0415f,
- 0.0556f, -0.2039f, 1.0569f };
- static MPPipeline *pipeline;
- mp_state_proc state_proc;
- static char burst_dir[23];
- static volatile bool is_capturing = false;
- static volatile int frames_processed = 0;
- static volatile int frames_received = 0;
- libmegapixels_camera *pr_camera;
- static int output_buffer_width = -1;
- static int output_buffer_height = -1;
- static bool flash_enabled;
- static char capture_fname[255];
- static GSettings *settings;
- void
- mp_process_find_all_processors(GtkListStore *store)
- {
- GtkTreeIter iter;
- char buffer[512];
- // Find all the original postprocess.sh locations
- // Check postprocess.sh in the current working directory
- if (access("./data/postprocess.sh", F_OK) != -1) {
- gtk_list_store_insert(store, &iter, -1);
- gtk_list_store_set(store,
- &iter,
- 0,
- "./data/postprocess.sh",
- 1,
- "(cwd) postprocess.sh",
- -1);
- }
- // Check for a script in XDG_CONFIG_HOME
- sprintf(buffer, "%s/megapixels/postprocess.sh", g_get_user_config_dir());
- if (access(buffer, F_OK) != -1) {
- gtk_list_store_insert(store, &iter, -1);
- gtk_list_store_set(
- store, &iter, 0, buffer, 1, "(user) postprocess.sh", -1);
- }
- // Check user overridden /etc/megapixels/postprocessor.sh
- sprintf(buffer, "%s/megapixels/postprocess.sh", SYSCONFDIR);
- if (access(buffer, F_OK) != -1) {
- gtk_list_store_insert(store, &iter, -1);
- gtk_list_store_set(
- store, &iter, 0, buffer, 1, "(system) postprocess.sh", -1);
- }
- // Check user overridden /usr/share/megapixels/postprocessor.sh
- sprintf(buffer, "%s/megapixels/postprocess.sh", DATADIR);
- if (access(buffer, F_OK) != -1) {
- gtk_list_store_insert(store, &iter, -1);
- gtk_list_store_set(
- store, &iter, 0, buffer, 1, "(built-in) postprocess.sh", -1);
- }
- // Find extra packaged postprocessor scripts
- // These should be packaged in
- // /usr/share/megapixels/postprocessor.d/executable
- sprintf(buffer, "%s/megapixels/postprocessor.d", DATADIR);
- DIR *d;
- struct dirent *dir;
- d = opendir(buffer);
- if (d) {
- while ((dir = readdir(d)) != NULL) {
- if (dir->d_name[0] == '.') {
- continue;
- }
- sprintf(buffer,
- "%s/megapixels/postprocessor.d/%s",
- DATADIR,
- dir->d_name);
- gtk_list_store_insert(store, &iter, -1);
- gtk_list_store_set(
- store, &iter, 0, buffer, 1, dir->d_name, -1);
- }
- closedir(d);
- }
- }
- bool
- mp_process_find_processor(char *script)
- {
- char filename[] = "postprocess.sh";
- // Check postprocess.sh in the current working directory
- sprintf(script, "./data/%s", filename);
- if (access(script, F_OK) != -1) {
- sprintf(script, "./data/%s", filename);
- printf("Found postprocessor script at %s\n", script);
- return true;
- }
- // Check for a script in XDG_CONFIG_HOME
- sprintf(script, "%s/megapixels/%s", g_get_user_config_dir(), filename);
- if (access(script, F_OK) != -1) {
- printf("Found postprocessor script at %s\n", script);
- return true;
- }
- // Check user overridden /etc/megapixels/postprocessor.sh
- sprintf(script, "%s/megapixels/%s", SYSCONFDIR, filename);
- if (access(script, F_OK) != -1) {
- printf("Found postprocessor script at %s\n", script);
- return true;
- }
- // Check packaged /usr/share/megapixels/postprocessor.sh
- sprintf(script, "%s/megapixels/%s", DATADIR, filename);
- if (access(script, F_OK) != -1) {
- printf("Found postprocessor script at %s\n", script);
- return true;
- }
- return false;
- }
- static void
- setup(MPPipeline *pipeline, const void *data)
- {
- libdng_init();
- settings = g_settings_new(APP_ID);
- prctl(PR_SET_NAME, "megapixels-pr", NULL, NULL, NULL);
- }
- void
- mp_process_pipeline_start()
- {
- pipeline = mp_pipeline_new();
- mp_pipeline_invoke(pipeline, setup, NULL, 0);
- mp_zbar_pipeline_start();
- }
- void
- mp_process_pipeline_stop()
- {
- mp_pipeline_free(pipeline);
- mp_zbar_pipeline_stop();
- }
- void
- mp_process_pipeline_sync()
- {
- mp_pipeline_sync(pipeline);
- }
- #define NUM_BUFFERS 4
- struct _MPProcessPipelineBuffer {
- GLuint texture_id;
- _Atomic(int) refcount;
- };
- static MPProcessPipelineBuffer output_buffers[NUM_BUFFERS];
- void
- mp_process_pipeline_buffer_ref(MPProcessPipelineBuffer *buf)
- {
- ++buf->refcount;
- }
- void
- mp_process_pipeline_buffer_unref(MPProcessPipelineBuffer *buf)
- {
- --buf->refcount;
- }
- uint32_t
- mp_process_pipeline_buffer_get_texture_id(MPProcessPipelineBuffer *buf)
- {
- return buf->texture_id;
- }
- static void
- repack_image_sequencial(const uint8_t *src_buf,
- uint8_t *dst_buf,
- libmegapixels_mode *mode)
- {
- uint16_t pixels[4];
- uint32_t row_length =
- libmegapixels_mode_width_to_bytes(mode->format, mode->width);
- uint32_t padding_bytes =
- libmegapixels_mode_width_to_padding(mode->format, mode->width);
- size_t si = 0;
- // Image data must be 10-bit packed
- assert(libmegapixels_format_bits_per_pixel(mode->format) == 10);
- /*
- * Repack 40 bits stored in sensor format into sequencial format
- *
- * src_buf: 11111111 22222222 33333333 44444444 11223344 ...
- * dst_buf: 11111111 11222222 22223333 33333344 44444444 ...
- */
- for (size_t i = 0; i < row_length * mode->height; i += 5) {
- // Skip padding bytes in source buffer
- if (i && i % row_length == 0)
- si += padding_bytes;
- /* Extract pixels from packed sensor format */
- pixels[0] = (src_buf[si] << 2) | (src_buf[si + 4] >> 6);
- pixels[1] = (src_buf[si + 1] << 2) | (src_buf[si + 4] >> 4 & 0x03);
- pixels[2] = (src_buf[si + 2] << 2) | (src_buf[si + 4] >> 2 & 0x03);
- pixels[3] = (src_buf[si + 3] << 2) | (src_buf[si + 4] & 0x03);
- /* Pack pixels into sequencial format */
- dst_buf[i] = (pixels[0] >> 2 & 0xff);
- dst_buf[i + 1] = (pixels[0] << 6 & 0xff) | (pixels[1] >> 4 & 0x3f);
- dst_buf[i + 2] = (pixels[1] << 4 & 0xff) | (pixels[2] >> 6 & 0x0f);
- dst_buf[i + 3] = (pixels[2] << 2 & 0xff) | (pixels[3] >> 8 & 0x03);
- dst_buf[i + 4] = (pixels[3] & 0xff);
- si += 5;
- }
- }
- static GLES2Debayer *gles2_debayer = NULL;
- static GdkGLContext *context;
- // #define RENDERDOC
- #ifdef RENDERDOC
- #include <renderdoc/app.h>
- extern RENDERDOC_API_1_1_2 *rdoc_api;
- #endif
- static void
- init_gl(MPPipeline *pipeline, GdkSurface **surface)
- {
- GError *error = NULL;
- context = gdk_surface_create_gl_context(*surface, &error);
- if (context == NULL) {
- printf("Failed to initialize OpenGL context: %s\n", error->message);
- g_clear_error(&error);
- return;
- }
- gdk_gl_context_set_use_es(context, true);
- gdk_gl_context_set_required_version(context, 2, 0);
- gdk_gl_context_set_forward_compatible(context, false);
- #ifdef DEBUG
- gdk_gl_context_set_debug_enabled(context, true);
- #else
- gdk_gl_context_set_debug_enabled(context, false);
- #endif
- gdk_gl_context_realize(context, &error);
- if (error != NULL) {
- printf("Failed to create OpenGL context: %s\n", error->message);
- g_clear_object(&context);
- g_clear_error(&error);
- return;
- }
- gdk_gl_context_make_current(context);
- check_gl();
- // Make a VAO for OpenGL
- if (!gdk_gl_context_get_use_es(context)) {
- GLuint vao;
- glGenVertexArrays(1, &vao);
- glBindVertexArray(vao);
- check_gl();
- }
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- check_gl();
- for (size_t i = 0; i < NUM_BUFFERS; ++i) {
- glGenTextures(1, &output_buffers[i].texture_id);
- glBindTexture(GL_TEXTURE_2D, output_buffers[i].texture_id);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- }
- glBindTexture(GL_TEXTURE_2D, 0);
- gboolean is_es = gdk_gl_context_get_use_es(context);
- int major, minor;
- gdk_gl_context_get_version(context, &major, &minor);
- printf("Initialized %s %d.%d\n",
- is_es ? "OpenGL ES" : "OpenGL",
- major,
- minor);
- }
- void
- mp_process_pipeline_init_gl(GdkSurface *surface)
- {
- mp_pipeline_invoke(pipeline,
- (MPPipelineCallback)init_gl,
- &surface,
- sizeof(GdkSurface *));
- }
- float
- clamp_float(float value, float min, float max)
- {
- if (value > max)
- return max;
- if (value < min)
- return min;
- return value;
- }
- static GdkTexture *
- process_image_for_preview(const uint8_t *image)
- {
- #ifdef PROFILE_DEBAYER
- clock_t t1 = clock();
- #endif
- // Pick an available buffer
- MPProcessPipelineBuffer *output_buffer = NULL;
- for (size_t i = 0; i < NUM_BUFFERS; ++i) {
- if (output_buffers[i].refcount == 0) {
- output_buffer = &output_buffers[i];
- }
- }
- if (output_buffer == NULL) {
- return NULL;
- }
- assert(output_buffer != NULL);
- #ifdef RENDERDOC
- if (rdoc_api) {
- rdoc_api->StartFrameCapture(NULL, NULL);
- }
- #endif
- // Copy image to a GL texture. TODO: This can be avoided
- GLuint input_texture;
- glGenTextures(1, &input_texture);
- glBindTexture(GL_TEXTURE_2D, input_texture);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexImage2D(
- GL_TEXTURE_2D,
- 0,
- GL_LUMINANCE,
- libmegapixels_mode_width_to_bytes(state_proc.mode->format,
- state_proc.mode->width) +
- libmegapixels_mode_width_to_padding(state_proc.mode->format,
- state_proc.mode->width),
- state_proc.mode->height,
- 0,
- GL_LUMINANCE,
- GL_UNSIGNED_BYTE,
- image);
- check_gl();
- gles2_debayer_process(
- gles2_debayer, output_buffer->texture_id, input_texture);
- check_gl();
- glFinish();
- glDeleteTextures(1, &input_texture);
- #ifdef PROFILE_DEBAYER
- clock_t t2 = clock();
- printf("process_image_for_preview %fms\n",
- (float)(t2 - t1) / CLOCKS_PER_SEC * 1000);
- #endif
- #ifdef RENDERDOC
- if (rdoc_api) {
- rdoc_api->EndFrameCapture(NULL, NULL);
- }
- #endif
- mp_process_pipeline_buffer_ref(output_buffer);
- mp_main_set_preview(output_buffer);
- if (!state_proc.exposure.manual && state_proc.exposure.auto_control == 0) {
- int width = output_buffer_width;
- int height = output_buffer_height / 3;
- uint32_t *center = g_malloc_n(width * height * sizeof(uint32_t), 1);
- glReadPixels(
- 0, height, width, height, GL_RGBA, GL_UNSIGNED_BYTE, center);
- libmegapixels_aaa_set_matrix(&state_proc.stats,
- state_proc.calibration.color_matrix_1,
- state_proc.calibration.color_matrix_2);
- libmegapixels_aaa_software_statistics(
- &state_proc.stats, center, width, height);
- state_proc.blacklevel -= (float)state_proc.stats.blacklevel * 0.001f;
- state_proc.blacklevel =
- clamp_float(state_proc.blacklevel, 0.0f, 0.07f);
- float r = state_proc.stats.avg_r;
- float g = state_proc.stats.avg_g;
- float b = state_proc.stats.avg_b;
- // Revert the current gains set on the preview
- b /= state_proc.red;
- b /= state_proc.blue;
- float t = 2.0f;
- if (r < t && g < t && b < t) {
- // Don't try to AWB on very dark frames
- } else {
- // Calculate the new R/B gains based on the average color of
- // the frame
- float new_r = g / clamp_float(r, 1.0f, 999.0f);
- float new_b = g / clamp_float(b, 1.0f, 999.0f);
- state_proc.red = clamp_float(new_r, 0.01f, 4.0f);
- state_proc.blue = clamp_float(new_b, 0.01f, 4.0f);
- }
- gles2_debayer_set_shading(gles2_debayer,
- state_proc.red,
- state_proc.blue,
- state_proc.blacklevel);
- }
- // Create a thumbnail from the preview for the last capture
- GdkTexture *thumb = NULL;
- if (state_proc.captures_remaining == 1) {
- printf("Making thumbnail\n");
- size_t size = output_buffer_width * output_buffer_height *
- sizeof(uint32_t);
- uint32_t *data = g_malloc_n(size, 1);
- glReadPixels(0,
- 0,
- output_buffer_width,
- output_buffer_height,
- GL_RGBA,
- GL_UNSIGNED_BYTE,
- data);
- check_gl();
- // Flip vertically
- for (size_t y = 0; y < output_buffer_height / 2; ++y) {
- for (size_t x = 0; x < output_buffer_width; ++x) {
- uint32_t tmp = data[(output_buffer_height - y - 1) *
- output_buffer_width +
- x];
- data[(output_buffer_height - y - 1) *
- output_buffer_width +
- x] = data[y * output_buffer_width + x];
- data[y * output_buffer_width + x] = tmp;
- }
- }
- thumb = gdk_memory_texture_new(output_buffer_width,
- output_buffer_height,
- GDK_MEMORY_R8G8B8A8,
- g_bytes_new_take(data, size),
- output_buffer_width *
- sizeof(uint32_t));
- }
- return thumb;
- }
- static void
- process_image_for_capture_yuv(const uint8_t *image, int count)
- {
- char fname[255];
- sprintf(fname, "%s/%d.jpg", burst_dir, count);
- FILE *outfile;
- if ((outfile = fopen(fname, "wb")) == NULL) {
- g_printerr("jpeg open %s: error %d, %s\n",
- fname,
- errno,
- strerror(errno));
- return;
- }
- int width = state_proc.mode->width;
- struct jpeg_compress_struct cinfo;
- struct jpeg_error_mgr jerr;
- cinfo.err = jpeg_std_error(&jerr);
- jpeg_create_compress(&cinfo);
- jpeg_stdio_dest(&cinfo, outfile);
- cinfo.image_width = state_proc.mode->width & -1;
- cinfo.image_height = state_proc.mode->height & -1;
- cinfo.input_components = 3;
- cinfo.in_color_space = JCS_YCbCr;
- jpeg_set_defaults(&cinfo);
- jpeg_set_quality(&cinfo, 92, TRUE);
- jpeg_start_compress(&cinfo, TRUE);
- uint8_t *row = malloc(width * 3);
- JSAMPROW row_pointer[1];
- row_pointer[0] = row;
- unsigned int y1 = 0;
- unsigned int u = 1;
- unsigned int y2 = 2;
- unsigned int v = 3;
- switch (state_proc.mode->v4l_pixfmt) {
- case V4L2_PIX_FMT_UYVY:
- u = 0;
- y1 = 1;
- v = 2;
- y2 = 3;
- break;
- case V4L2_PIX_FMT_YUYV:
- y1 = 0;
- u = 1;
- y2 = 2;
- v = 3;
- break;
- case V4L2_PIX_FMT_YVYU:
- y1 = 0;
- v = 1;
- y2 = 2;
- u = 3;
- break;
- case V4L2_PIX_FMT_VYUY:
- v = 0;
- y1 = 1;
- u = 2;
- y2 = 3;
- break;
- }
- while (cinfo.next_scanline < cinfo.image_height) {
- unsigned int i, j = 0;
- unsigned int offset = cinfo.next_scanline * cinfo.image_width * 2;
- for (i = 0; i < cinfo.image_width * 2; i += 4) {
- row[j + 0] = image[offset + i + y1];
- row[j + 1] = image[offset + i + u];
- row[j + 2] = image[offset + i + v];
- row[j + 3] = image[offset + i + y2];
- row[j + 4] = image[offset + i + u];
- row[j + 5] = image[offset + i + v];
- j += 6;
- }
- jpeg_write_scanlines(&cinfo, row_pointer, 1);
- }
- jpeg_finish_compress(&cinfo);
- fclose(outfile);
- jpeg_destroy_compress(&cinfo);
- }
- static void
- process_image_for_capture_bayer(const uint8_t *image, int count)
- {
- char fname[255];
- sprintf(fname, "%s/%d.dng", burst_dir, count);
- uint16_t orientation;
- if (state_proc.device_rotation == 0) {
- orientation = state_proc.mode->mirrored ?
- LIBDNG_ORIENTATION_TOPRIGHT :
- LIBDNG_ORIENTATION_TOPLEFT;
- } else if (state_proc.device_rotation == 90) {
- orientation = state_proc.mode->mirrored ?
- LIBDNG_ORIENTATION_RIGHTBOT :
- LIBDNG_ORIENTATION_LEFTBOT;
- } else if (state_proc.device_rotation == 180) {
- orientation = state_proc.mode->mirrored ?
- LIBDNG_ORIENTATION_BOTLEFT :
- LIBDNG_ORIENTATION_BOTRIGHT;
- } else {
- orientation = state_proc.mode->mirrored ?
- LIBDNG_ORIENTATION_LEFTTOP :
- LIBDNG_ORIENTATION_RIGHTTOP;
- }
- libdng_info dng = { 0 };
- libdng_new(&dng);
- libdng_set_datetime_now(&dng);
- libdng_set_mode_from_pixfmt(&dng, state_proc.mode->v4l_pixfmt);
- if (state_proc.configuration->make != NULL &&
- state_proc.configuration->model != NULL) {
- libdng_set_make_model(&dng,
- state_proc.configuration->make,
- state_proc.configuration->model);
- }
- libdng_set_orientation(&dng, orientation);
- libdng_set_software(&dng, "Megapixels");
- libdng_set_neutral(&dng, state_proc.red, 1.0f, state_proc.blue);
- libdng_set_analog_balance(&dng,
- state_proc.balance[0],
- state_proc.balance[1],
- state_proc.balance[2]);
- if (!state_proc.exposure.manual) {
- libdng_set_exposure_program(&dng, LIBDNG_EXPOSUREPROGRAM_NORMAL);
- } else {
- libdng_set_exposure_program(&dng, LIBDNG_EXPOSUREPROGRAM_MANUAL);
- }
- printf("Writing frame to %s\n", fname);
- libdng_write(&dng,
- fname,
- state_proc.mode->width,
- state_proc.mode->height,
- image,
- count);
- libdng_free(&dng);
- /*
- TIFFSetField(tif,
- EXIFTAG_EXPOSURETIME,
- (mode.frame_interval.numerator /
- (float)mode.frame_interval.denominator) /
- ((float)mode.height / (float)exposure));
- if (pr_camera->iso_min && pr_camera->iso_max) {
- uint16_t isospeed = remap(
- gain - 1, 0, gain_max, pr_camera->iso_min,
- pr_camera->iso_max); TIFFSetField(tif, EXIFTAG_ISOSPEEDRATINGS, 1,
- &isospeed);
- }
- if (!pr_camera->has_flash) {
- // No flash function
- TIFFSetField(tif, EXIFTAG_FLASH, 0x20);
- } else if (flash_enabled) {
- // Flash present and fired
- TIFFSetField(tif, EXIFTAG_FLASH, 0x1);
- } else {
- // Flash present but not fired
- TIFFSetField(tif, EXIFTAG_FLASH, 0x0);
- }
- */
- /*
- if (pr_camera->fnumber) {
- TIFFSetField(tif, EXIFTAG_FNUMBER, pr_camera->fnumber);
- }
- if (pr_camera->focallength) {
- TIFFSetField(tif, EXIFTAG_FOCALLENGTH, pr_camera->focallength);
- }
- if (pr_camera->focallength && pr_camera->cropfactor) {
- TIFFSetField(tif,
- EXIFTAG_FOCALLENGTHIN35MMFILM,
- (short)(pr_camera->focallength *
- pr_camera->cropfactor));
- }
- */
- }
- static void
- process_image_for_capture(const uint8_t *image, int count)
- {
- switch (state_proc.mode->v4l_pixfmt) {
- case V4L2_PIX_FMT_UYVY:
- case V4L2_PIX_FMT_YUYV:
- case V4L2_PIX_FMT_YVYU:
- case V4L2_PIX_FMT_VYUY:
- process_image_for_capture_yuv(image, count);
- break;
- default:
- process_image_for_capture_bayer(image, count);
- break;
- }
- }
- static void
- post_process_finished(GSubprocess *proc, GAsyncResult *res, GdkTexture *thumb)
- {
- char *stdout;
- g_subprocess_communicate_utf8_finish(proc, res, &stdout, NULL, NULL);
- // The last line contains the file name
- int end = strlen(stdout);
- // Skip the newline at the end
- stdout[--end] = '\0';
- char *path = path = stdout + end - 1;
- do {
- if (*path == '\n') {
- path++;
- break;
- }
- --path;
- } while (path > stdout);
- mp_main_capture_completed(thumb, path);
- }
- static void
- process_capture_burst(GdkTexture *thumb)
- {
- time_t rawtime;
- time(&rawtime);
- struct tm tim = *(localtime(&rawtime));
- char timestamp[30];
- strftime(timestamp, 30, "%Y%m%d%H%M%S", &tim);
- if (g_get_user_special_dir(G_USER_DIRECTORY_PICTURES) != NULL) {
- sprintf(capture_fname,
- "%s/IMG%s",
- g_get_user_special_dir(G_USER_DIRECTORY_PICTURES),
- timestamp);
- } else if (getenv("XDG_PICTURES_DIR") != NULL) {
- sprintf(capture_fname,
- "%s/IMG%s",
- getenv("XDG_PICTURES_DIR"),
- timestamp);
- } else {
- sprintf(capture_fname,
- "%s/Pictures/IMG%s",
- getenv("HOME"),
- timestamp);
- }
- bool save_dng = g_settings_get_boolean(settings, "save-raw");
- char *postprocessor = g_settings_get_string(settings, "postprocessor");
- char save_dng_s[2] = "0";
- if (save_dng) {
- save_dng_s[0] = '1';
- }
- // Start post-processing the captured burst
- g_print("Post process %s to %s.ext (save-dng %s)\n",
- burst_dir,
- capture_fname,
- save_dng_s);
- g_autoptr(GError) error = NULL;
- GSubprocess *proc = g_subprocess_new(G_SUBPROCESS_FLAGS_STDOUT_PIPE,
- &error,
- postprocessor,
- burst_dir,
- capture_fname,
- save_dng_s,
- NULL);
- if (!proc) {
- g_printerr("Failed to spawn postprocess process: %s\n",
- error->message);
- return;
- }
- g_subprocess_communicate_utf8_async(
- proc, NULL, NULL, (GAsyncReadyCallback)post_process_finished, thumb);
- }
- static void
- process_image(MPPipeline *pipeline, const MPBuffer *buffer)
- {
- #ifdef PROFILE_PROCESS
- clock_t t1 = clock();
- #endif
- size_t size = (libmegapixels_mode_width_to_bytes(state_proc.mode->format,
- state_proc.mode->width) +
- libmegapixels_mode_width_to_padding(state_proc.mode->format,
- state_proc.mode->width)) *
- state_proc.mode->height;
- uint8_t *image = malloc(size);
- memcpy(image, buffer->data, size);
- mp_io_pipeline_release_buffer(buffer->index);
- MPZBarImage *zbar_image = mp_zbar_image_new(image,
- state_proc.mode->format,
- state_proc.mode->width,
- state_proc.mode->height,
- state_proc.device_rotation,
- state_proc.mode->mirrored);
- mp_zbar_pipeline_process_image(mp_zbar_image_ref(zbar_image));
- #ifdef PROFILE_PROCESS
- clock_t t2 = clock();
- #endif
- GdkTexture *thumb = process_image_for_preview(image);
- if (state_proc.captures_remaining > 0) {
- --state_proc.captures_remaining;
- process_image_for_capture(image, state_proc.counter++);
- if (state_proc.captures_remaining == 0) {
- assert(thumb);
- process_capture_burst(thumb);
- } else {
- assert(!thumb);
- }
- } else {
- assert(!thumb);
- }
- mp_zbar_image_unref(zbar_image);
- ++frames_processed;
- if (state_proc.captures_remaining == 0) {
- is_capturing = false;
- }
- #ifdef PROFILE_PROCESS
- clock_t t3 = clock();
- printf("process_image %fms, step 1:%fms, step 2:%fms\n",
- (float)(t3 - t1) / CLOCKS_PER_SEC * 1000,
- (float)(t2 - t1) / CLOCKS_PER_SEC * 1000,
- (float)(t3 - t2) / CLOCKS_PER_SEC * 1000);
- #endif
- }
- void
- mp_process_pipeline_process_image(MPBuffer buffer)
- {
- // If we haven't processed the previous frame yet, drop this one
- if (frames_received != frames_processed && !is_capturing) {
- mp_io_pipeline_release_buffer(buffer.index);
- return;
- }
- ++frames_received;
- mp_pipeline_invoke(pipeline,
- (MPPipelineCallback)process_image,
- &buffer,
- sizeof(MPBuffer));
- }
- static void
- capture()
- {
- char template[] = "/tmp/megapixels.XXXXXX";
- char *tempdir;
- tempdir = mkdtemp(template);
- if (tempdir == NULL) {
- g_printerr("Could not make capture directory %s\n", template);
- exit(EXIT_FAILURE);
- }
- strcpy(burst_dir, tempdir);
- state_proc.captures_remaining = state_proc.burst_length;
- state_proc.counter = 0;
- }
- void
- mp_process_pipeline_capture()
- {
- is_capturing = true;
- mp_pipeline_invoke(pipeline, capture, NULL, 0);
- }
- static void
- on_output_changed(bool format_changed)
- {
- output_buffer_width = state_proc.mode->width / 2;
- output_buffer_height = state_proc.mode->height / 2;
- if (state_proc.mode->rotation != 0 && state_proc.mode->rotation != 180) {
- int tmp = output_buffer_width;
- output_buffer_width = output_buffer_height;
- output_buffer_height = tmp;
- }
- for (size_t i = 0; i < NUM_BUFFERS; ++i) {
- glBindTexture(GL_TEXTURE_2D, output_buffers[i].texture_id);
- glTexImage2D(GL_TEXTURE_2D,
- 0,
- GL_RGBA,
- output_buffer_width,
- output_buffer_height,
- 0,
- GL_RGBA,
- GL_UNSIGNED_BYTE,
- NULL);
- }
- glBindTexture(GL_TEXTURE_2D, 0);
- // Create new gles2_debayer on format change
- if (format_changed) {
- if (gles2_debayer)
- gles2_debayer_free(gles2_debayer);
- gles2_debayer = gles2_debayer_new(state_proc.mode->format);
- check_gl();
- gles2_debayer_use(gles2_debayer);
- }
- state_proc.blacklevel = 0.0f;
- state_proc.red = 1.0f;
- state_proc.blue = 1.0f;
- gles2_debayer_configure(gles2_debayer,
- output_buffer_width,
- output_buffer_height,
- state_proc.mode->width,
- state_proc.mode->height,
- state_proc.mode->rotation,
- 0,
- state_proc.calibration);
- }
- static int
- mod(int a, int b)
- {
- int r = a % b;
- return r < 0 ? r + b : r;
- }
- static void
- update_state(MPPipeline *pipeline, const mp_state_proc *new_state)
- {
- bool camera_changed = state_proc.camera != new_state->camera;
- state_proc.configuration = new_state->configuration;
- state_proc.camera = new_state->camera;
- state_proc.gain.control = new_state->gain.control;
- state_proc.gain.auto_control = new_state->gain.auto_control;
- state_proc.gain.value = new_state->gain.value;
- state_proc.gain.max = new_state->gain.max;
- state_proc.gain.manual = new_state->gain.manual;
- state_proc.exposure.control = new_state->exposure.control;
- state_proc.exposure.auto_control = new_state->exposure.auto_control;
- state_proc.exposure.value = new_state->exposure.value;
- state_proc.exposure.max = new_state->exposure.max;
- state_proc.exposure.manual = new_state->exposure.manual;
- state_proc.focus.control = new_state->focus.control;
- state_proc.focus.auto_control = new_state->focus.auto_control;
- state_proc.focus.value = new_state->focus.value;
- state_proc.focus.max = new_state->focus.max;
- state_proc.focus.manual = new_state->focus.manual;
- const bool output_changed =
- !libmegapixels_mode_equals(state_proc.mode,
- new_state->camera->current_mode) ||
- state_proc.preview_width != new_state->preview_width ||
- state_proc.preview_height != new_state->preview_height ||
- state_proc.device_rotation != new_state->device_rotation;
- bool format_changed = state_proc.mode == NULL;
- if (!format_changed && state_proc.mode->v4l_pixfmt !=
- new_state->camera->current_mode->v4l_pixfmt) {
- format_changed = true;
- }
- state_proc.mode = new_state->camera->current_mode;
- state_proc.preview_width = new_state->preview_width;
- state_proc.preview_height = new_state->preview_height;
- state_proc.device_rotation = new_state->device_rotation;
- state_proc.burst_length = new_state->burst_length;
- state_proc.balance[0] = new_state->balance[0];
- state_proc.balance[1] = new_state->balance[1];
- state_proc.balance[2] = new_state->balance[2];
- if (output_changed) {
- state_proc.camera_rotation = mod(
- state_proc.mode->rotation - state_proc.device_rotation, 360);
- on_output_changed(format_changed);
- }
- if (camera_changed) {
- char cf[PATH_MAX];
- if (find_calibration(cf, state_proc.camera->name)) {
- state_proc.calibration = parse_calibration_file(cf);
- } else {
- printf("No calibration for %s\n", state_proc.camera->name);
- }
- }
- mp_state_main new_main = {
- .camera = pr_camera,
- .has_auto_focus_continuous = false,
- .has_auto_focus_start = false,
- .preview_buffer_width = output_buffer_width,
- .preview_buffer_height = output_buffer_height,
- .control_flash = false,
- .gain.control = state_proc.gain.control,
- .gain.auto_control = state_proc.gain.auto_control,
- .gain.value = state_proc.gain.value,
- .gain.max = state_proc.gain.max,
- .gain.manual = state_proc.gain.manual,
- .exposure.control = state_proc.exposure.control,
- .exposure.auto_control = state_proc.exposure.auto_control,
- .exposure.value = state_proc.exposure.value,
- .exposure.max = state_proc.exposure.max,
- .exposure.manual = state_proc.exposure.manual,
- .focus.control = state_proc.focus.control,
- .focus.auto_control = state_proc.focus.auto_control,
- .focus.value = state_proc.focus.value,
- .focus.max = state_proc.focus.max,
- .focus.manual = state_proc.focus.manual,
- .stats.exposure = state_proc.stats.exposure,
- .stats.temp = state_proc.stats.temp,
- .stats.tint = state_proc.stats.tint,
- .stats.focus = state_proc.stats.focus,
- };
- mp_main_update_state(&new_main);
- }
- void
- mp_process_pipeline_update_state(const mp_state_proc *new_state)
- {
- mp_pipeline_invoke(pipeline,
- (MPPipelineCallback)update_state,
- new_state,
- sizeof(mp_state_proc));
- }
- // GTK4 seems to require this
- void
- pango_fc_font_get_languages()
- {
- }
|