diff options
Diffstat (limited to 'plug-ins/common/depth-merge.c')
-rw-r--r-- | plug-ins/common/depth-merge.c | 1050 |
1 files changed, 1050 insertions, 0 deletions
diff --git a/plug-ins/common/depth-merge.c b/plug-ins/common/depth-merge.c new file mode 100644 index 0000000..ff65693 --- /dev/null +++ b/plug-ins/common/depth-merge.c @@ -0,0 +1,1050 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * Depth Merge -- Combine two image layers via corresponding depth maps + * Copyright (C) 1997, 1998 Sean Cier (scier@PostHorizon.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* Version 1.0.0: (14 August 1998) + * Math optimizations, miscellaneous speedups + * + * Version 0.1: (6 July 1997) + * Initial Release + */ + +#include "config.h" + +#include <libgimp/gimp.h> +#include <libgimp/gimpui.h> + +#include "libgimp/stdplugins-intl.h" + + +#define DEBUG + +#ifndef LERP +#define LERP(frac,a,b) ((frac)*(b) + (1-(frac))*(a)) +#endif + +#define MUL255(i) ((i)*256 - (i)) +#define DIV255(i) (((i) + (i)/256 + 1) / 256) + +#define PLUG_IN_PROC "plug-in-depth-merge" +#define PLUG_IN_VERSION "August 1998" +#define PLUG_IN_BINARY "depth-merge" +#define PLUG_IN_ROLE "gimp-depth-merge" + +#define PREVIEW_SIZE 256 + +/* ----- DepthMerge ----- */ + +struct _DepthMerge; + +typedef struct _DepthMergeInterface +{ + gboolean active; + + GtkWidget *dialog; + + GtkWidget *preview; + gint previewWidth; + gint previewHeight; + + guchar *previewSource1; + guchar *previewSource2; + guchar *previewDepthMap1; + guchar *previewDepthMap2; +} DepthMergeInterface; + +typedef struct _DepthMergeParams +{ + gint32 result; + gint32 source1; + gint32 source2; + gint32 depthMap1; + gint32 depthMap2; + gfloat overlap; + gfloat offset; + gfloat scale1; + gfloat scale2; +} DepthMergeParams; + +typedef struct _DepthMerge +{ + DepthMergeInterface *interface; + DepthMergeParams params; + + gint32 resultDrawable_id; + gint32 source1Drawable_id; + gint32 source2Drawable_id; + gint32 depthMap1Drawable_id; + gint32 depthMap2Drawable_id; + gint selectionX; + gint selectionY; + gint selectionWidth; + gint selectionHeight; + gint resultHasAlpha; +} DepthMerge; + +static void DepthMerge_initParams (DepthMerge *dm); +static gboolean DepthMerge_construct (DepthMerge *dm); +static void DepthMerge_destroy (DepthMerge *dm); +static gint32 DepthMerge_execute (DepthMerge *dm); +static void DepthMerge_executeRegion (DepthMerge *dm, + guchar *source1Row, + guchar *source2Row, + guchar *depthMap1Row, + guchar *depthMap2Row, + guchar *resultRow, + gint length); +static gboolean DepthMerge_dialog (DepthMerge *dm); +static void DepthMerge_buildPreviewSourceImage (DepthMerge *dm); +static void DepthMerge_updatePreview (DepthMerge *dm); + + +static gboolean dm_constraint (gint32 imageId, + gint32 drawableId, + gpointer data); + +static void dialogSource1ChangedCallback (GtkWidget *widget, DepthMerge *dm); +static void dialogSource2ChangedCallback (GtkWidget *widget, DepthMerge *dm); +static void dialogDepthMap1ChangedCallback (GtkWidget *widget, DepthMerge *dm); +static void dialogDepthMap2ChangedCallback (GtkWidget *widget, DepthMerge *dm); + +static void dialogValueScaleUpdateCallback (GtkAdjustment *adjustment, + gpointer data); + +static void util_fillReducedBuffer (guchar *dest, + const Babl *dest_format, + gint destWidth, + gint destHeight, + gint32 sourceDrawable_id, + gint x0, + gint y0, + gint sourceWidth, + gint sourceHeight); + + +/* ----- plug-in entry points ----- */ + +static void query (void); +static void run (const gchar *name, + gint nparams, + const GimpParam *param, + gint *nreturn_vals, + GimpParam **return_vals); + +const GimpPlugInInfo PLUG_IN_INFO = +{ + NULL, /* init_proc */ + NULL, /* quit_proc */ + query, /* query_proc */ + run /* run_proc */ +}; + +MAIN () + +static void +query (void) +{ + static const GimpParamDef args[] = + { + { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" }, + { GIMP_PDB_IMAGE, "image", "Input image (unused)" }, + { GIMP_PDB_DRAWABLE, "result", "Result" }, + { GIMP_PDB_DRAWABLE, "source1", "Source 1" }, + { GIMP_PDB_DRAWABLE, "source2", "Source 2" }, + { GIMP_PDB_DRAWABLE, "depthMap1", "Depth map 1" }, + { GIMP_PDB_DRAWABLE, "depthMap2", "Depth map 2" }, + { GIMP_PDB_FLOAT, "overlap", "Overlap" }, + { GIMP_PDB_FLOAT, "offset", "Depth relative offset" }, + { GIMP_PDB_FLOAT, "scale1", "Depth relative scale 1" }, + { GIMP_PDB_FLOAT, "scale2", "Depth relative scale 2" } + }; + + gimp_install_procedure (PLUG_IN_PROC, + N_("Combine two images using depth maps (z-buffers)"), + "Taking as input two full-color, full-alpha " + "images and two corresponding grayscale depth " + "maps, this plug-in combines the images based " + "on which is closer (has a lower depth map value) " + "at each point.", + "Sean Cier", + "Sean Cier", + PLUG_IN_VERSION, + N_("_Depth Merge..."), + "RGB*, GRAY*", + GIMP_PLUGIN, + G_N_ELEMENTS (args), 0, + args, NULL); + + gimp_plugin_menu_register (PLUG_IN_PROC, "<Image>/Filters/Combine"); +} + +static void +run (const gchar *name, + gint numParams, + const GimpParam *param, + gint *numReturnVals, + GimpParam **returnVals) +{ + static GimpParam values[1]; + GimpRunMode runMode; + GimpPDBStatusType status = GIMP_PDB_SUCCESS; + DepthMerge dm; + + INIT_I18N (); + gegl_init (NULL, NULL); + + runMode = param[0].data.d_int32; + + *numReturnVals = 1; + *returnVals = values; + + switch (runMode) + { + case GIMP_RUN_INTERACTIVE: + DepthMerge_initParams (&dm); + gimp_get_data (PLUG_IN_PROC, &(dm.params)); + dm.params.result = param[2].data.d_drawable; + if (!DepthMerge_construct (&dm)) + return; + + if (!DepthMerge_dialog (&dm)) + { + values[0].type = GIMP_PDB_STATUS; + values[0].data.d_status = GIMP_PDB_SUCCESS; + return; + } + break; + + case GIMP_RUN_NONINTERACTIVE: + DepthMerge_initParams (&dm); + if (numParams != 11) + status = GIMP_PDB_CALLING_ERROR; + else + { + dm.params.result = param[ 2].data.d_drawable; + dm.params.source1 = param[ 3].data.d_drawable; + dm.params.source2 = param[ 4].data.d_drawable; + dm.params.depthMap1 = param[ 5].data.d_drawable; + dm.params.depthMap2 = param[ 6].data.d_drawable; + dm.params.overlap = param[ 7].data.d_float; + dm.params.offset = param[ 8].data.d_float; + dm.params.scale1 = param[ 9].data.d_float; + dm.params.scale2 = param[10].data.d_float; + } + if (!DepthMerge_construct (&dm)) + return; + break; + + case GIMP_RUN_WITH_LAST_VALS: + DepthMerge_initParams (&dm); + gimp_get_data (PLUG_IN_PROC, &(dm.params)); + if (!DepthMerge_construct (&dm)) + return; + break; + + default: + status = GIMP_PDB_CALLING_ERROR; + } + + if (status == GIMP_PDB_SUCCESS) + { + if (!DepthMerge_execute (&dm)) + { + status = GIMP_PDB_EXECUTION_ERROR; + } + else + { + if (runMode != GIMP_RUN_NONINTERACTIVE) + gimp_displays_flush (); + + if (runMode == GIMP_RUN_INTERACTIVE) + gimp_set_data (PLUG_IN_PROC, + &(dm.params), sizeof (DepthMergeParams)); + } + } + + DepthMerge_destroy (&dm); + + values[0].data.d_status = status; +} + +/* ----- DepthMerge ----- */ + +static void +DepthMerge_initParams (DepthMerge *dm) +{ + dm->params.result = -1; + dm->params.source1 = -1; + dm->params.source2 = -1; + dm->params.depthMap1 = -1; + dm->params.depthMap2 = -1; + dm->params.overlap = 0; + dm->params.offset = 0; + dm->params.scale1 = 1; + dm->params.scale2 = 1; +} + +static gboolean +DepthMerge_construct (DepthMerge *dm) +{ + dm->interface = NULL; + + dm->resultDrawable_id = dm->params.result; + + if (! gimp_drawable_mask_intersect (dm->resultDrawable_id, + &(dm->selectionX), &(dm->selectionY), + &(dm->selectionWidth), + &(dm->selectionHeight))) + { + return FALSE; + } + + dm->resultHasAlpha = gimp_drawable_has_alpha (dm->resultDrawable_id); + + dm->source1Drawable_id = dm->params.source1; + dm->source2Drawable_id = dm->params.source2; + dm->depthMap1Drawable_id = dm->params.depthMap1; + dm->depthMap2Drawable_id = dm->params.depthMap2; + + dm->params.overlap = CLAMP (dm->params.overlap, 0, 2); + dm->params.offset = CLAMP (dm->params.offset, -1, 1); + dm->params.scale1 = CLAMP (dm->params.scale1, -1, 1); + dm->params.scale2 = CLAMP (dm->params.scale2, -1, 1); + + return TRUE; +} + +static void +DepthMerge_destroy (DepthMerge *dm) +{ + if (dm->interface != NULL) + { + g_free (dm->interface->previewSource1); + g_free (dm->interface->previewSource2); + g_free (dm->interface->previewDepthMap1); + g_free (dm->interface->previewDepthMap2); + g_free (dm->interface); + } +} + +static gint32 +DepthMerge_execute (DepthMerge *dm) +{ + int x, y; + GeglBuffer *source1_buffer = NULL; + GeglBuffer *source2_buffer = NULL; + GeglBuffer *depthMap1_buffer = NULL; + GeglBuffer *depthMap2_buffer = NULL; + GeglBuffer *result_buffer; + guchar *source1Row, *source2Row; + guchar *depthMap1Row, *depthMap2Row; + guchar *resultRow; + guchar *tempRow; + + gimp_progress_init (_("Depth-merging")); + + resultRow = g_new (guchar, dm->selectionWidth * 4); + source1Row = g_new (guchar, dm->selectionWidth * 4); + source2Row = g_new (guchar, dm->selectionWidth * 4); + depthMap1Row = g_new (guchar, dm->selectionWidth ); + depthMap2Row = g_new (guchar, dm->selectionWidth ); + tempRow = g_new (guchar, dm->selectionWidth * 4); + + if (dm->source1Drawable_id > 0) + { + source1_buffer = gimp_drawable_get_buffer (dm->source1Drawable_id); + } + else + { + for (x = 0; x < dm->selectionWidth; x++) + { + source1Row[4 * x ] = 0; + source1Row[4 * x + 1] = 0; + source1Row[4 * x + 2] = 0; + source1Row[4 * x + 3] = 255; + } + } + + if (dm->source2Drawable_id > 0) + { + source2_buffer = gimp_drawable_get_buffer (dm->source2Drawable_id); + } + else + { + for (x = 0; x < dm->selectionWidth; x++) + { + source2Row[4 * x ] = 0; + source2Row[4 * x + 1] = 0; + source2Row[4 * x + 2] = 0; + source2Row[4 * x + 3] = 255; + } + } + + if (dm->depthMap1Drawable_id > 0) + { + depthMap1_buffer = gimp_drawable_get_buffer (dm->depthMap1Drawable_id); + } + else + { + for (x = 0; x < dm->selectionWidth; x++) + { + depthMap1Row[x] = 0; + } + } + + if (dm->depthMap2Drawable_id > 0) + { + depthMap2_buffer = gimp_drawable_get_buffer (dm->depthMap2Drawable_id); + } + else + { + for (x = 0; x < dm->selectionWidth; x++) + { + depthMap2Row[x] = 0; + } + } + + result_buffer = gimp_drawable_get_shadow_buffer (dm->resultDrawable_id); + + for (y = dm->selectionY; y < (dm->selectionY + dm->selectionHeight); y++) + { + if (dm->source1Drawable_id > 0) + { + gegl_buffer_get (source1_buffer, + GEGL_RECTANGLE (dm->selectionX, y, + dm->selectionWidth, 1), 1.0, + babl_format ("R'G'B'A u8"), source1Row, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + } + + if (dm->source2Drawable_id > 0) + { + gegl_buffer_get (source2_buffer, + GEGL_RECTANGLE (dm->selectionX, y, + dm->selectionWidth, 1), 1.0, + babl_format ("R'G'B'A u8"), source2Row, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + } + + if (dm->depthMap1Drawable_id > 0) + { + gegl_buffer_get (depthMap1_buffer, + GEGL_RECTANGLE (dm->selectionX, y, + dm->selectionWidth, 1), 1.0, + babl_format ("Y' u8"), depthMap1Row, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + } + + if (dm->depthMap2Drawable_id > 0) + { + gegl_buffer_get (depthMap2_buffer, + GEGL_RECTANGLE (dm->selectionX, y, + dm->selectionWidth, 1), 1.0, + babl_format ("Y' u8"), depthMap2Row, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + } + + DepthMerge_executeRegion (dm, + source1Row, source2Row, depthMap1Row, depthMap2Row, + resultRow, + dm->selectionWidth); + + gegl_buffer_set (result_buffer, + GEGL_RECTANGLE (dm->selectionX, y, + dm->selectionWidth, 1), 0, + babl_format ("R'G'B'A u8"), resultRow, + GEGL_AUTO_ROWSTRIDE); + + gimp_progress_update ((double)(y-dm->selectionY) / + (double)(dm->selectionHeight - 1)); + } + + g_free (resultRow); + g_free (source1Row); + g_free (source2Row); + g_free (depthMap1Row); + g_free (depthMap2Row); + g_free (tempRow); + + gimp_progress_update (1.0); + + if (source1_buffer) + g_object_unref (source1_buffer); + + if (source2_buffer) + g_object_unref (source2_buffer); + + if (depthMap1_buffer) + g_object_unref (depthMap1_buffer); + + if (depthMap2_buffer) + g_object_unref (depthMap2_buffer); + + g_object_unref (result_buffer); + + gimp_drawable_merge_shadow (dm->resultDrawable_id, TRUE); + gimp_drawable_update (dm->resultDrawable_id, + dm->selectionX, dm->selectionY, + dm->selectionWidth, dm->selectionHeight); + + return TRUE; +} + +static void +DepthMerge_executeRegion (DepthMerge *dm, + guchar *source1Row, + guchar *source2Row, + guchar *depthMap1Row, + guchar *depthMap2Row, + guchar *resultRow, + gint length) +{ + gfloat scale1, scale2, offset255, invOverlap255; + gfloat frac, depth1, depth2; + gushort c1[4], c2[4]; + gushort cR1[4] = { 0, 0, 0, 0 }, cR2[4] = { 0, 0, 0, 0 }; + gushort cR[4], temp; + gint i, tempInt; + + invOverlap255 = 1.0 / (MAX (dm->params.overlap, 0.001) * 255); + offset255 = dm->params.offset * 255; + scale1 = dm->params.scale1; + scale2 = dm->params.scale2; + + for (i = 0; i < length; i++) + { + depth1 = (gfloat) depthMap1Row[i]; + depth2 = (gfloat) depthMap2Row[i]; + + frac = (depth2 * scale2 - (depth1 * scale1 + offset255)) * invOverlap255; + frac = 0.5 * (frac + 1.0); + frac = CLAMP(frac, 0.0, 1.0); + + /* c1 -> color corresponding to source1 */ + c1[0] = source1Row[4 * i ]; + c1[1] = source1Row[4 * i + 1]; + c1[2] = source1Row[4 * i + 2]; + c1[3] = source1Row[4 * i + 3]; + + /* c2 -> color corresponding to source2 */ + c2[0] = source2Row[4 * i ]; + c2[1] = source2Row[4 * i + 1]; + c2[2] = source2Row[4 * i + 2]; + c2[3] = source2Row[4 * i + 3]; + + if (frac != 0) + { + /* cR1 -> result if c1 is completely on top */ + cR1[0] = c1[3] * c1[0] + (255 - c1[3]) * c2[0]; + cR1[1] = c1[3] * c1[1] + (255 - c1[3]) * c2[1]; + cR1[2] = c1[3] * c1[2] + (255 - c1[3]) * c2[2]; + cR1[3] = MUL255 (c1[3]) + (255 - c1[3]) * c2[3]; + } + + if (frac != 1) + { + /* cR2 -> result if c2 is completely on top */ + cR2[0] = c2[3] * c2[0] + (255 - c2[3]) * c1[0]; + cR2[1] = c2[3] * c2[1] + (255 - c2[3]) * c1[1]; + cR2[2] = c2[3] * c2[2] + (255 - c2[3]) * c1[2]; + cR2[3] = MUL255 (c2[3]) + (255 - c2[3]) * c1[3]; + } + + if (frac == 1) + { + cR[0] = cR1[0]; + cR[1] = cR1[1]; + cR[2] = cR1[2]; + cR[3] = cR1[3]; + } + else if (frac == 0) + { + cR[0] = cR2[0]; + cR[1] = cR2[1]; + cR[2] = cR2[2]; + cR[3] = cR2[3]; + } + else + { + tempInt = LERP (frac, cR2[0], cR1[0]); + cR[0] = CLAMP (tempInt,0,255 * 255); + tempInt = LERP (frac, cR2[1], cR1[1]); + cR[1] = CLAMP (tempInt,0,255 * 255); + tempInt = LERP (frac, cR2[2], cR1[2]); + cR[2] = CLAMP (tempInt,0,255 * 255); + tempInt = LERP (frac, cR2[3], cR1[3]); + cR[3] = CLAMP (tempInt,0,255 * 255); + } + + temp = DIV255 (cR[0]); resultRow[4 * i ] = MIN (temp, 255); + temp = DIV255 (cR[1]); resultRow[4 * i + 1] = MIN (temp, 255); + temp = DIV255 (cR[2]); resultRow[4 * i + 2] = MIN (temp, 255); + temp = DIV255 (cR[3]); resultRow[4 * i + 3] = MIN (temp, 255); + } +} + +static gboolean +DepthMerge_dialog (DepthMerge *dm) +{ + GtkWidget *dialog; + GtkWidget *vbox; + GtkWidget *frame; + GtkWidget *table; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *combo; + GtkObject *adj; + gboolean run; + + dm->interface = g_new0 (DepthMergeInterface, 1); + + gimp_ui_init (PLUG_IN_BINARY, TRUE); + + dm->interface->dialog = + dialog = gimp_dialog_new (_("Depth Merge"), PLUG_IN_ROLE, + NULL, 0, + gimp_standard_help_func, PLUG_IN_PROC, + + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_OK"), GTK_RESPONSE_OK, + + NULL); + + gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog), + GTK_RESPONSE_OK, + GTK_RESPONSE_CANCEL, + -1); + + gimp_window_set_transient (GTK_WINDOW (dialog)); + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 12); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), + vbox, FALSE, FALSE, 0); + gtk_widget_show (vbox); + + /* Preview */ + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN); + gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + dm->interface->previewWidth = MIN (dm->selectionWidth, PREVIEW_SIZE); + dm->interface->previewHeight = MIN (dm->selectionHeight, PREVIEW_SIZE); + dm->interface->preview = gimp_preview_area_new (); + gtk_widget_set_size_request (dm->interface->preview, + dm->interface->previewWidth, + dm->interface->previewHeight); + gtk_container_add (GTK_CONTAINER (frame), dm->interface->preview); + gtk_widget_show (dm->interface->preview); + + DepthMerge_buildPreviewSourceImage (dm); + + /* Source and Depth Map selection */ + table = gtk_table_new (8, 3, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_table_set_row_spacings (GTK_TABLE (table), 6); + gtk_table_set_row_spacing (GTK_TABLE (table), 1, 12); + gtk_table_set_row_spacing (GTK_TABLE (table), 3, 12); + gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0); + gtk_widget_show (table); + + label = gtk_label_new (_("Source 1:")); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, + GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show (label); + + combo = gimp_drawable_combo_box_new (dm_constraint, dm); + gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), dm->params.source1, + G_CALLBACK (dialogSource1ChangedCallback), + dm); + + gtk_table_attach (GTK_TABLE (table), combo, 1, 3, 0, 1, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_widget_show (combo); + + label = gtk_label_new(_("Depth map:")); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2, + GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show (label); + + combo = gimp_drawable_combo_box_new (dm_constraint, dm); + gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), dm->params.depthMap1, + G_CALLBACK (dialogDepthMap1ChangedCallback), + dm); + + gtk_table_attach (GTK_TABLE (table), combo, 1, 3, 1, 2, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_widget_show (combo); + + label = gtk_label_new (_("Source 2:")); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3, + GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show (label); + + combo = gimp_drawable_combo_box_new (dm_constraint, dm); + gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), dm->params.source2, + G_CALLBACK (dialogSource2ChangedCallback), + dm); + + gtk_table_attach (GTK_TABLE (table), combo, 1, 3, 2, 3, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_widget_show (combo); + + label = gtk_label_new (_("Depth map:")); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4, + GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show (label); + + combo = gimp_drawable_combo_box_new (dm_constraint, dm); + gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), dm->params.depthMap2, + G_CALLBACK (dialogDepthMap2ChangedCallback), + dm); + + gtk_table_attach (GTK_TABLE (table), combo, 1, 3, 3, 4, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_widget_show (combo); + + /* Numeric parameters */ + adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 4, + _("O_verlap:"), 0, 6, + dm->params.overlap, 0, 2, 0.001, 0.01, 3, + TRUE, 0, 0, + NULL, NULL); + g_signal_connect (adj, "value-changed", + G_CALLBACK (dialogValueScaleUpdateCallback), + &(dm->params.overlap)); + g_object_set_data (G_OBJECT (adj), "dm", dm); + + adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 5, + _("O_ffset:"), 0, 6, + dm->params.offset, -1, 1, 0.001, 0.01, 3, + TRUE, 0, 0, + NULL, NULL); + g_signal_connect (adj, "value-changed", + G_CALLBACK (dialogValueScaleUpdateCallback), + &(dm->params.offset)); + g_object_set_data (G_OBJECT (adj), "dm", dm); + + adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 6, + _("Sc_ale 1:"), 0, 6, + dm->params.scale1, -1, 1, 0.001, 0.01, 3, + TRUE, 0, 0, + NULL, NULL); + g_signal_connect (adj, "value-changed", + G_CALLBACK (dialogValueScaleUpdateCallback), + &(dm->params.scale1)); + g_object_set_data (G_OBJECT (adj), "dm", dm); + + adj = gimp_scale_entry_new (GTK_TABLE (table), 0, 7, + _("Sca_le 2:"), 0, 6, + dm->params.scale2, -1, 1, 0.001, 0.01, 3, + TRUE, 0, 0, + NULL, NULL); + g_signal_connect (adj, "value-changed", + G_CALLBACK (dialogValueScaleUpdateCallback), + &(dm->params.scale2)); + g_object_set_data (G_OBJECT (adj), "dm", dm); + + dm->interface->active = TRUE; + + gtk_widget_show (dm->interface->dialog); + DepthMerge_updatePreview (dm); + + run = (gimp_dialog_run (GIMP_DIALOG (dm->interface->dialog)) == GTK_RESPONSE_OK); + + gtk_widget_destroy (dm->interface->dialog); + dm->interface->dialog = NULL; + + return run; +} + +static void +DepthMerge_buildPreviewSourceImage (DepthMerge *dm) +{ + dm->interface->previewSource1 = + g_new (guchar, dm->interface->previewWidth * + dm->interface->previewHeight * 4); + util_fillReducedBuffer (dm->interface->previewSource1, + babl_format ("R'G'B'A u8"), + dm->interface->previewWidth, + dm->interface->previewHeight, + dm->source1Drawable_id, + dm->selectionX, dm->selectionY, + dm->selectionWidth, dm->selectionHeight); + + dm->interface->previewSource2 = + g_new (guchar, dm->interface->previewWidth * + dm->interface->previewHeight * 4); + util_fillReducedBuffer (dm->interface->previewSource2, + babl_format ("R'G'B'A u8"), + dm->interface->previewWidth, + dm->interface->previewHeight, + dm->source2Drawable_id, + dm->selectionX, dm->selectionY, + dm->selectionWidth, dm->selectionHeight); + + dm->interface->previewDepthMap1 = + g_new (guchar, dm->interface->previewWidth * + dm->interface->previewHeight * 1); + util_fillReducedBuffer (dm->interface->previewDepthMap1, + babl_format ("Y' u8"), + dm->interface->previewWidth, + dm->interface->previewHeight, + dm->depthMap1Drawable_id, + dm->selectionX, dm->selectionY, + dm->selectionWidth, dm->selectionHeight); + + dm->interface->previewDepthMap2 = + g_new (guchar, dm->interface->previewWidth * + dm->interface->previewHeight * 1); + util_fillReducedBuffer (dm->interface->previewDepthMap2, + babl_format ("Y' u8"), + dm->interface->previewWidth, + dm->interface->previewHeight, + dm->depthMap2Drawable_id, + dm->selectionX, dm->selectionY, + dm->selectionWidth, dm->selectionHeight); +} + +static void +DepthMerge_updatePreview (DepthMerge *dm) +{ + gint y; + guchar *source1Row, *source2Row; + guchar *depthMap1Row, *depthMap2Row; + guchar *resultRGBA; + + if (!dm->interface->active) + return; + + resultRGBA = g_new (guchar, 4 * dm->interface->previewWidth * + dm->interface->previewHeight); + + for (y = 0; y < dm->interface->previewHeight; y++) + { + source1Row = + &(dm->interface->previewSource1[ y * dm->interface->previewWidth * 4]); + source2Row = + &(dm->interface->previewSource2[ y * dm->interface->previewWidth * 4]); + depthMap1Row = + &(dm->interface->previewDepthMap1[y * dm->interface->previewWidth ]); + depthMap2Row = + &(dm->interface->previewDepthMap2[y * dm->interface->previewWidth ]); + + DepthMerge_executeRegion(dm, + source1Row, source2Row, depthMap1Row, depthMap2Row, + resultRGBA + 4 * y * dm->interface->previewWidth, + dm->interface->previewWidth); + } + + gimp_preview_area_draw (GIMP_PREVIEW_AREA (dm->interface->preview), + 0, 0, + dm->interface->previewWidth, + dm->interface->previewHeight, + GIMP_RGBA_IMAGE, + resultRGBA, + dm->interface->previewWidth * 4); + g_free(resultRGBA); +} + +/* ----- Callbacks ----- */ + +static gboolean +dm_constraint (gint32 imageId, + gint32 drawableId, + gpointer data) +{ + DepthMerge *dm = (DepthMerge *)data; + + return ((drawableId == -1) || + ((gimp_drawable_width (drawableId) == + gimp_drawable_width (dm->params.result)) && + (gimp_drawable_height (drawableId) == + gimp_drawable_height (dm->params.result)) && + ((gimp_drawable_is_rgb (drawableId) && + (gimp_drawable_is_rgb (dm->params.result))) || + gimp_drawable_is_gray (drawableId)))); +} + +static void +dialogSource1ChangedCallback (GtkWidget *widget, + DepthMerge *dm) +{ + gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), + &dm->params.source1); + + dm->source1Drawable_id = dm->params.source1; + + util_fillReducedBuffer (dm->interface->previewSource1, + babl_format ("R'G'B'A u8"), + dm->interface->previewWidth, + dm->interface->previewHeight, + dm->source1Drawable_id, + dm->selectionX, dm->selectionY, + dm->selectionWidth, dm->selectionHeight); + + DepthMerge_updatePreview (dm); +} + +static void +dialogSource2ChangedCallback (GtkWidget *widget, + DepthMerge *dm) +{ + gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), + &dm->params.source2); + + dm->source2Drawable_id = dm->params.source2; + + util_fillReducedBuffer (dm->interface->previewSource2, + babl_format ("R'G'B'A u8"), + dm->interface->previewWidth, + dm->interface->previewHeight, + dm->source2Drawable_id, + dm->selectionX, dm->selectionY, + dm->selectionWidth, dm->selectionHeight); + + DepthMerge_updatePreview (dm); +} + +static void +dialogDepthMap1ChangedCallback (GtkWidget *widget, + DepthMerge *dm) +{ + gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), + &dm->params.depthMap1); + + dm->depthMap1Drawable_id = dm->params.depthMap1; + + util_fillReducedBuffer (dm->interface->previewDepthMap1, + babl_format ("Y' u8"), + dm->interface->previewWidth, + dm->interface->previewHeight, + dm->depthMap1Drawable_id, + dm->selectionX, dm->selectionY, + dm->selectionWidth, dm->selectionHeight); + + DepthMerge_updatePreview (dm); +} + +static void +dialogDepthMap2ChangedCallback (GtkWidget *widget, + DepthMerge *dm) +{ + gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (widget), + &dm->params.depthMap2); + + dm->depthMap2Drawable_id = dm->params.depthMap2;; + + util_fillReducedBuffer (dm->interface->previewDepthMap2, + babl_format ("Y' u8"), + dm->interface->previewWidth, + dm->interface->previewHeight, + dm->depthMap2Drawable_id, + dm->selectionX, dm->selectionY, + dm->selectionWidth, dm->selectionHeight); + + DepthMerge_updatePreview (dm); +} + +static void +dialogValueScaleUpdateCallback (GtkAdjustment *adjustment, + gpointer data) +{ + DepthMerge *dm = g_object_get_data (G_OBJECT (adjustment), "dm"); + + gimp_float_adjustment_update (adjustment, data); + + DepthMerge_updatePreview (dm); +} + +/* ----- Utility routines ----- */ + +static void +util_fillReducedBuffer (guchar *dest, + const Babl *dest_format, + gint destWidth, + gint destHeight, + gint32 sourceDrawable_id, + gint x0, + gint y0, + gint sourceWidth, + gint sourceHeight) +{ + GeglBuffer *buffer; + guchar *sourceBuffer, *reducedRowBuffer; + guchar *sourceBufferPos, *reducedRowBufferPos; + guchar *sourceBufferRow; + gint x, y, i, yPrime; + gint destBPP; + gint *sourceRowOffsetLookup; + + destBPP = babl_format_get_bytes_per_pixel (dest_format); + + if ((sourceDrawable_id < 1) || (sourceWidth == 0) || (sourceHeight == 0)) + { + for (x = 0; x < destWidth * destHeight * destBPP; x++) + dest[x] = 0; + + return; + } + + sourceBuffer = g_new (guchar, sourceWidth * sourceHeight * destBPP); + reducedRowBuffer = g_new (guchar, destWidth * destBPP); + sourceRowOffsetLookup = g_new (int, destWidth); + + buffer = gimp_drawable_get_buffer (sourceDrawable_id); + + for (x = 0; x < destWidth; x++) + sourceRowOffsetLookup[x] = (x * (sourceWidth - 1) / (destWidth - 1)) * destBPP; + + gegl_buffer_get (buffer, + GEGL_RECTANGLE (x0, y0, sourceWidth, sourceHeight), 1.0, + dest_format, sourceBuffer, + GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); + + for (y = 0; y < destHeight; y++) + { + yPrime = y * (sourceHeight - 1) / (destHeight - 1); + sourceBufferRow = &(sourceBuffer[yPrime * sourceWidth * destBPP]); + reducedRowBufferPos = reducedRowBuffer; + + for (x = 0; x < destWidth; x++) + { + sourceBufferPos = sourceBufferRow + sourceRowOffsetLookup[x]; + for (i = 0; i < destBPP; i++) + reducedRowBufferPos[i] = sourceBufferPos[i]; + reducedRowBufferPos += destBPP; + } + + memcpy (&(dest[y * destWidth * destBPP]), reducedRowBuffer, + destWidth * destBPP); + } + + g_object_unref (buffer); + + g_free (sourceBuffer); + g_free (reducedRowBuffer); + g_free (sourceRowOffsetLookup); +} |