mirror of https://github.com/GNOME/gimp.git
1210 lines
42 KiB
C
1210 lines
42 KiB
C
/* TODO:
|
|
* - Debug
|
|
* - Clean up source
|
|
* - Package, distribute
|
|
*/
|
|
|
|
/* Depth Merge -- Combine two image layers via corresponding depth maps
|
|
* Copyright (C) 1997 Sean Cier (scier@cmu.edu)
|
|
*
|
|
* A plug-in for The GIMP
|
|
* The GIMP is Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
*
|
|
* 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 2 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 0.1: (6 July 1997)
|
|
* Initial Release
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "gtk/gtk.h"
|
|
#include "libgimp/gimp.h"
|
|
#include "libgimp/gimpui.h"
|
|
|
|
|
|
#define DEBUG
|
|
|
|
#ifndef LERP
|
|
#define LERP(frac,a,b) ((frac)*(b) + (1-(frac))*(a))
|
|
#endif
|
|
|
|
|
|
#define PLUG_IN_NAME "plug_in_depth_merge"
|
|
#define PLUG_IN_TITLE "Depth Merge"
|
|
#define PLUG_IN_VERSION "0.1; 6 July 1997"
|
|
|
|
#define PREVIEW_SIZE 256
|
|
#define SCALE_WIDTH 200
|
|
#define ENTRY_WIDTH 60
|
|
#define CHECK_SIZE 8
|
|
#define CHECK_DARK 255*1/3
|
|
#define CHECK_LIGHT 255*2/3
|
|
|
|
|
|
|
|
/* ----- DepthMerge ----- */
|
|
|
|
struct _DepthMerge;
|
|
|
|
typedef struct _DepthMergeValueEdit {
|
|
struct _DepthMerge *dm;
|
|
float *value;
|
|
GtkAdjustment *scaleData;
|
|
GtkWidget *entry;
|
|
float min, max;
|
|
} DepthMergeValueEdit;
|
|
|
|
typedef struct _DepthMergeInterface {
|
|
gint active;
|
|
|
|
GtkWidget *dialog;
|
|
|
|
GtkWidget *preview;
|
|
gint previewWidth;
|
|
gint previewHeight;
|
|
|
|
DepthMergeValueEdit *overlapValueEdit;
|
|
DepthMergeValueEdit *offsetValueEdit;
|
|
DepthMergeValueEdit *scale1ValueEdit;
|
|
DepthMergeValueEdit *scale2ValueEdit;
|
|
|
|
guchar *checkRow0, *checkRow1;
|
|
guchar *previewSource1, *previewSource2,
|
|
*previewDepthMap1, *previewDepthMap2;
|
|
|
|
gint run;
|
|
} DepthMergeInterface;
|
|
|
|
typedef struct _DepthMergeParams {
|
|
gint32 result;
|
|
gint32 source1, source2;
|
|
gint32 depthMap1, depthMap2;
|
|
gfloat overlap;
|
|
gfloat offset;
|
|
gfloat scale1, scale2;
|
|
} DepthMergeParams;
|
|
|
|
|
|
typedef struct _DepthMerge {
|
|
DepthMergeInterface *interface;
|
|
DepthMergeParams params;
|
|
|
|
GDrawable *resultDrawable,
|
|
*source1Drawable, *source2Drawable,
|
|
*depthMap1Drawable, *depthMap2Drawable;
|
|
gint selectionX0, selectionY0, selectionX1, selectionY1,
|
|
selectionWidth, selectionHeight;
|
|
gint resultHasAlpha;
|
|
} DepthMerge;
|
|
|
|
void DepthMerge_initParams( DepthMerge *dm);
|
|
void DepthMerge_construct( DepthMerge *dm);
|
|
void DepthMerge_destroy( DepthMerge *dm);
|
|
gint32 DepthMerge_execute( DepthMerge *dm);
|
|
void DepthMerge_executeRegion( DepthMerge *dm,
|
|
guchar *source1Row,
|
|
guchar *source2Row,
|
|
guchar *depthMap1Row,
|
|
guchar *depthMap2Row,
|
|
guchar *resultRow,
|
|
gint length);
|
|
gint32 DepthMerge_dialog( DepthMerge *dm);
|
|
DepthMergeValueEdit *
|
|
DepthMerge_createValueEdit( DepthMerge *dm,
|
|
char *title, GtkTable *table,
|
|
int row, int startCol,
|
|
float *value,
|
|
float min, float max, float step);
|
|
void DepthMerge_buildPreviewSourceImage(DepthMerge *dm);
|
|
void DepthMerge_updatePreview( DepthMerge *dm);
|
|
|
|
|
|
gint constraintResultSizeAndResultColorOrGray(gint32 imageId,
|
|
gint32 drawableId, gpointer data);
|
|
gint constraintResultSizeAndGray(gint32 imageId,
|
|
gint32 drawableId, gpointer data);
|
|
void dialogOkCallback( GtkWidget *widget, gpointer data);
|
|
void dialogCancelCallback(GtkWidget *widget, gpointer data);
|
|
void dialogCloseCallback( GtkWidget *widget, gpointer data);
|
|
void dialogSource1ChangedCallback( gint32 id, gpointer data);
|
|
void dialogSource2ChangedCallback( gint32 id, gpointer data);
|
|
void dialogDepthMap1ChangedCallback(gint32 id, gpointer data);
|
|
void dialogDepthMap2ChangedCallback(gint32 id, gpointer data);
|
|
void dialogValueScaleUpdateCallback(GtkAdjustment *adjustment, gpointer data);
|
|
void dialogValueEntryUpdateCallback(GtkWidget *widget, gpointer data);
|
|
|
|
|
|
void util_fillReducedBuffer(guchar *dest, gint destWidth, gint destHeight,
|
|
gint destBPP, gint destHasAlpha,
|
|
GDrawable *sourceDrawable,
|
|
gint x0, gint y0,
|
|
gint sourceWidth, gint sourceHeight);
|
|
void util_convertColorspace(guchar *dest,
|
|
gint destBPP, gint destHasAlpha,
|
|
guchar *source,
|
|
gint sourceBPP, gint sourceHasAlpha,
|
|
gint length);
|
|
|
|
/* ----- plug-in entry points ----- */
|
|
|
|
static void query();
|
|
static void run(char *name,
|
|
int numParams, GParam *param,
|
|
int *numReturnVals, GParam **returnVals);
|
|
|
|
GPlugInInfo PLUG_IN_INFO = {
|
|
NULL, /* init_proc */
|
|
NULL, /* quit_proc */
|
|
query, /* query_proc */
|
|
run /* run_proc */
|
|
};
|
|
|
|
MAIN()
|
|
|
|
static void query() {
|
|
static GParamDef args[] = {
|
|
{ PARAM_INT32, "run_mode", "Interactive, non-interactive" },
|
|
{ PARAM_IMAGE, "image", "Input image (unused)" },
|
|
{ PARAM_DRAWABLE, "result", "Result" },
|
|
{ PARAM_DRAWABLE, "source1", "Source 1" },
|
|
{ PARAM_DRAWABLE, "source2", "Source 2" },
|
|
{ PARAM_DRAWABLE, "depthMap1", "Depth map 1" },
|
|
{ PARAM_DRAWABLE, "depthMap2", "Depth map 2" },
|
|
{ PARAM_FLOAT, "overlap", "Overlap" },
|
|
{ PARAM_FLOAT, "offset", "Depth relative offset" },
|
|
{ PARAM_FLOAT, "scale1", "Depth relative scale 1" },
|
|
{ PARAM_FLOAT, "scale2", "Depth relative scale 2" },
|
|
};
|
|
static int numArgs = sizeof(args) / sizeof(GParamDef);
|
|
|
|
gimp_install_procedure(PLUG_IN_NAME,
|
|
"Combine two images using corresponding depth maps (z-buffers)",
|
|
"Taking as input two full-colour, 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,
|
|
"<Image>/Filters/Combine/Depth Merge",
|
|
"RGB*, GRAY*",
|
|
PROC_PLUG_IN,
|
|
numArgs,
|
|
0,
|
|
args,
|
|
NULL);
|
|
}
|
|
|
|
|
|
static void run(char *name,
|
|
int numParams, GParam *param,
|
|
int *numReturnVals, GParam **returnVals) {
|
|
static GParam values[1];
|
|
GRunModeType runMode;
|
|
GStatusType status;
|
|
DepthMerge dm;
|
|
|
|
runMode = (GRunModeType)param[0].data.d_int32;
|
|
status = STATUS_SUCCESS;
|
|
*numReturnVals = 1;
|
|
*returnVals = values;
|
|
|
|
switch (runMode) {
|
|
case RUN_INTERACTIVE:
|
|
DepthMerge_initParams(&dm);
|
|
gimp_get_data(PLUG_IN_NAME, &(dm.params));
|
|
dm.params.result = param[2].data.d_drawable;
|
|
DepthMerge_construct(&dm);
|
|
if (!DepthMerge_dialog(&dm)) {
|
|
values[0].type = PARAM_STATUS;
|
|
values[0].data.d_status = STATUS_SUCCESS;
|
|
return;
|
|
}
|
|
break;
|
|
case RUN_NONINTERACTIVE:
|
|
DepthMerge_initParams(&dm);
|
|
if (numParams != 11)
|
|
status = STATUS_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;
|
|
}
|
|
DepthMerge_construct(&dm);
|
|
break;
|
|
case RUN_WITH_LAST_VALS:
|
|
DepthMerge_initParams(&dm);
|
|
gimp_get_data(PLUG_IN_NAME, &(dm.params));
|
|
DepthMerge_construct(&dm);
|
|
break;
|
|
default:
|
|
status = STATUS_CALLING_ERROR;
|
|
}
|
|
|
|
if (status == STATUS_SUCCESS) {
|
|
gimp_tile_cache_ntiles((dm.resultDrawable->width + gimp_tile_width() - 1) /
|
|
gimp_tile_width());
|
|
|
|
if (!DepthMerge_execute(&dm))
|
|
status = STATUS_EXECUTION_ERROR;
|
|
else {
|
|
if (runMode != RUN_NONINTERACTIVE)
|
|
gimp_displays_flush();
|
|
if (runMode == RUN_INTERACTIVE)
|
|
gimp_set_data(PLUG_IN_NAME, &(dm.params), sizeof(DepthMergeParams));
|
|
}
|
|
}
|
|
|
|
DepthMerge_destroy(&dm);
|
|
|
|
values[0].data.d_status = status;
|
|
}
|
|
|
|
|
|
/* ----- DepthMerge ----- */
|
|
|
|
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;
|
|
}
|
|
|
|
void DepthMerge_construct(DepthMerge *dm) {
|
|
dm->interface = NULL;
|
|
|
|
dm->resultDrawable = gimp_drawable_get(dm->params.result);
|
|
gimp_drawable_mask_bounds(dm->resultDrawable->id,
|
|
&(dm->selectionX0), &(dm->selectionY0),
|
|
&(dm->selectionX1), &(dm->selectionY1));
|
|
dm->selectionWidth = dm->selectionX1 - dm->selectionX0;
|
|
dm->selectionHeight = dm->selectionY1 - dm->selectionY0;
|
|
dm->resultHasAlpha = gimp_drawable_has_alpha(dm->resultDrawable->id);
|
|
|
|
dm->source1Drawable = (dm->params.source1 == -1) ? NULL :
|
|
gimp_drawable_get(dm->params.source1 );
|
|
dm->source2Drawable = (dm->params.source2 == -1) ? NULL :
|
|
gimp_drawable_get(dm->params.source2 );
|
|
dm->depthMap1Drawable = (dm->params.depthMap1 == -1) ? NULL :
|
|
gimp_drawable_get(dm->params.depthMap1);
|
|
dm->depthMap2Drawable = (dm->params.depthMap2 == -1) ? NULL :
|
|
gimp_drawable_get(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);
|
|
}
|
|
|
|
void DepthMerge_destroy(DepthMerge *dm) {
|
|
if (dm->interface != NULL) {
|
|
g_free(dm->interface->overlapValueEdit);
|
|
g_free(dm->interface->offsetValueEdit );
|
|
g_free(dm->interface->scale1ValueEdit );
|
|
g_free(dm->interface->scale2ValueEdit );
|
|
g_free(dm->interface->checkRow0);
|
|
g_free(dm->interface->checkRow1);
|
|
g_free(dm->interface->previewSource1 );
|
|
g_free(dm->interface->previewSource2 );
|
|
g_free(dm->interface->previewDepthMap1);
|
|
g_free(dm->interface->previewDepthMap2);
|
|
g_free(dm->interface);
|
|
}
|
|
if (dm->resultDrawable != NULL)
|
|
gimp_drawable_detach(dm->resultDrawable);
|
|
if (dm->source1Drawable != NULL)
|
|
gimp_drawable_detach(dm->source1Drawable );
|
|
if (dm->source2Drawable != NULL)
|
|
gimp_drawable_detach(dm->source2Drawable );
|
|
if (dm->depthMap1Drawable != NULL)
|
|
gimp_drawable_detach(dm->depthMap1Drawable);
|
|
if (dm->depthMap2Drawable != NULL)
|
|
gimp_drawable_detach(dm->depthMap2Drawable);
|
|
}
|
|
|
|
|
|
gint32 DepthMerge_execute(DepthMerge *dm) {
|
|
int x, y;
|
|
GPixelRgn source1Rgn, source2Rgn, depthMap1Rgn, depthMap2Rgn,
|
|
resultRgn;
|
|
guchar *source1Row, *source2Row, *depthMap1Row, *depthMap2Row,
|
|
*resultRow,
|
|
*tempRow;
|
|
gint source1HasAlpha, source2HasAlpha,
|
|
depthMap1HasAlpha, depthMap2HasAlpha;
|
|
|
|
/* initialize */
|
|
|
|
source1HasAlpha = 0;
|
|
source2HasAlpha = 0;
|
|
depthMap1HasAlpha = 0;
|
|
depthMap2HasAlpha = 0;
|
|
|
|
gimp_progress_init("Depth-merging...");
|
|
|
|
resultRow = (guchar *)g_malloc(dm->selectionWidth * 4);
|
|
source1Row = (guchar *)g_malloc(dm->selectionWidth * 4);
|
|
source2Row = (guchar *)g_malloc(dm->selectionWidth * 4);
|
|
depthMap1Row = (guchar *)g_malloc(dm->selectionWidth );
|
|
depthMap2Row = (guchar *)g_malloc(dm->selectionWidth );
|
|
tempRow = (guchar *)g_malloc(dm->selectionWidth * 4);
|
|
|
|
if (dm->source1Drawable != NULL) {
|
|
source1HasAlpha = gimp_drawable_has_alpha(dm->source1Drawable->id);
|
|
gimp_pixel_rgn_init(&source1Rgn, dm->source1Drawable,
|
|
dm->selectionX0, dm->selectionY0,
|
|
dm->selectionWidth, dm->selectionHeight,
|
|
FALSE, FALSE);
|
|
} 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 != NULL) {
|
|
source2HasAlpha = gimp_drawable_has_alpha(dm->source2Drawable->id);
|
|
gimp_pixel_rgn_init(&source2Rgn, dm->source2Drawable,
|
|
dm->selectionX0, dm->selectionY0,
|
|
dm->selectionWidth, dm->selectionHeight,
|
|
FALSE, FALSE);
|
|
} 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 != NULL) {
|
|
depthMap1HasAlpha = gimp_drawable_has_alpha(dm->depthMap1Drawable->id);
|
|
gimp_pixel_rgn_init(&depthMap1Rgn, dm->depthMap1Drawable,
|
|
dm->selectionX0, dm->selectionY0,
|
|
dm->selectionWidth, dm->selectionHeight,
|
|
FALSE, FALSE);
|
|
} else
|
|
for (x = 0; x < dm->selectionWidth; x++) {
|
|
depthMap1Row[x ] = 0;
|
|
}
|
|
if (dm->depthMap2Drawable != NULL) {
|
|
depthMap2HasAlpha = gimp_drawable_has_alpha(dm->depthMap2Drawable->id);
|
|
gimp_pixel_rgn_init(&depthMap2Rgn, dm->depthMap2Drawable,
|
|
dm->selectionX0, dm->selectionY0,
|
|
dm->selectionWidth, dm->selectionHeight,
|
|
FALSE, FALSE);
|
|
} else
|
|
for (x = 0; x < dm->selectionWidth; x++) {
|
|
depthMap2Row[x ] = 0;
|
|
}
|
|
gimp_pixel_rgn_init(&resultRgn, dm->resultDrawable,
|
|
dm->selectionX0, dm->selectionY0,
|
|
dm->selectionWidth, dm->selectionHeight,
|
|
TRUE, TRUE);
|
|
|
|
for (y = dm->selectionY0; y < dm->selectionY1; y++) {
|
|
if (dm->source1Drawable != NULL) {
|
|
gimp_pixel_rgn_get_row(&source1Rgn, tempRow,
|
|
dm->selectionX0, y,
|
|
dm->selectionWidth);
|
|
util_convertColorspace(source1Row, 4, TRUE,
|
|
tempRow,
|
|
dm->source1Drawable->bpp, source1HasAlpha,
|
|
dm->selectionWidth);
|
|
}
|
|
if (dm->source2Drawable != NULL) {
|
|
gimp_pixel_rgn_get_row(&source2Rgn, tempRow,
|
|
dm->selectionX0, y,
|
|
dm->selectionWidth);
|
|
util_convertColorspace(source2Row, 4, TRUE,
|
|
tempRow,
|
|
dm->source2Drawable->bpp, source2HasAlpha,
|
|
dm->selectionWidth);
|
|
}
|
|
if (dm->depthMap1Drawable != NULL) {
|
|
gimp_pixel_rgn_get_row(&depthMap1Rgn, tempRow,
|
|
dm->selectionX0, y,
|
|
dm->selectionWidth);
|
|
util_convertColorspace(depthMap1Row, 1, FALSE,
|
|
tempRow,
|
|
dm->depthMap1Drawable->bpp, depthMap1HasAlpha,
|
|
dm->selectionWidth);
|
|
}
|
|
if (dm->depthMap2Drawable != NULL) {
|
|
gimp_pixel_rgn_get_row(&depthMap2Rgn, tempRow,
|
|
dm->selectionX0, y,
|
|
dm->selectionWidth);
|
|
util_convertColorspace(depthMap2Row, 1, FALSE,
|
|
tempRow,
|
|
dm->depthMap2Drawable->bpp, depthMap2HasAlpha,
|
|
dm->selectionWidth);
|
|
}
|
|
|
|
DepthMerge_executeRegion(dm,
|
|
source1Row, source2Row, depthMap1Row, depthMap2Row,
|
|
resultRow,
|
|
dm->selectionWidth);
|
|
util_convertColorspace(tempRow, dm->resultDrawable->bpp, dm->resultHasAlpha,
|
|
resultRow, 4, TRUE,
|
|
dm->selectionWidth);
|
|
|
|
gimp_pixel_rgn_set_row(&resultRgn, tempRow,
|
|
dm->selectionX0, y,
|
|
dm->selectionWidth);
|
|
|
|
gimp_progress_update((double)(y-dm->selectionY0) / (double)(dm->selectionHeight-1));
|
|
}
|
|
|
|
g_free(resultRow);
|
|
g_free(source1Row);
|
|
g_free(source2Row);
|
|
g_free(depthMap1Row);
|
|
g_free(depthMap2Row);
|
|
g_free(tempRow);
|
|
|
|
gimp_drawable_flush(dm->resultDrawable);
|
|
gimp_drawable_merge_shadow(dm->resultDrawable->id, TRUE);
|
|
gimp_drawable_update(dm->resultDrawable->id,
|
|
dm->selectionX0, dm->selectionY0,
|
|
dm->selectionWidth, dm->selectionHeight);
|
|
return(TRUE);
|
|
}
|
|
|
|
void DepthMerge_executeRegion(DepthMerge *dm,
|
|
guchar *source1Row, guchar *source2Row,
|
|
guchar *depthMap1Row, guchar *depthMap2Row,
|
|
guchar *resultRow, gint length) {
|
|
float scale1, scale2, offset, invOverlap;
|
|
float frac, depth1, depth2;
|
|
unsigned short c1[4], c2[4], cR1[4], cR2[4], cR[4], temp;
|
|
int i, tempInt;
|
|
|
|
invOverlap = 1.0 / MAX(dm->params.overlap, 0.001);
|
|
offset = dm->params.offset;
|
|
scale1 = dm->params.scale1;
|
|
scale2 = dm->params.scale2;
|
|
|
|
for (i = 0; i < length; i++) {
|
|
depth1 = depthMap1Row[i] * (1.0/255.0);
|
|
depth2 = depthMap2Row[i] * (1.0/255.0);
|
|
|
|
frac = (depth2*scale2 - (depth1*scale1 + offset)) * invOverlap;
|
|
frac = (frac+1.0)/2.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] = 255 *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] = 255 *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 = cR[0]/255; resultRow[4*i ] = CLAMP(temp, 0, 255);
|
|
temp = cR[1]/255; resultRow[4*i+1] = CLAMP(temp, 0, 255);
|
|
temp = cR[2]/255; resultRow[4*i+2] = CLAMP(temp, 0, 255);
|
|
temp = cR[3]/255; resultRow[4*i+3] = CLAMP(temp, 0, 255);
|
|
}
|
|
}
|
|
|
|
gint32 DepthMerge_dialog(DepthMerge *dm) {
|
|
GtkWidget *topTable;
|
|
GtkWidget *previewFrame;
|
|
GtkWidget *sourceTable;
|
|
GtkWidget *tempLabel;
|
|
GtkWidget *tempOptionMenu;
|
|
GtkWidget *tempMenu;
|
|
GtkWidget *numericParameterTable;
|
|
GtkWidget *tempButton;
|
|
gint argc;
|
|
gchar **argv;
|
|
guchar *color_cube;
|
|
|
|
dm->interface = (DepthMergeInterface *)g_malloc(sizeof(DepthMergeInterface));
|
|
dm->interface->active = FALSE;
|
|
dm->interface->run = FALSE;
|
|
|
|
argc = 1;
|
|
argv = g_new(gchar *, 1);
|
|
argv[0] = g_strdup(PLUG_IN_NAME);
|
|
|
|
gtk_init(&argc, &argv);
|
|
gtk_rc_parse(gimp_gtkrc());
|
|
|
|
gdk_set_use_xshm(gimp_use_xshm());
|
|
|
|
gtk_preview_set_gamma(gimp_gamma());
|
|
gtk_preview_set_install_cmap(gimp_install_cmap());
|
|
color_cube = gimp_color_cube();
|
|
gtk_preview_set_color_cube(color_cube[0],
|
|
color_cube[1],
|
|
color_cube[2],
|
|
color_cube[3]);
|
|
|
|
gtk_widget_set_default_visual(gtk_preview_get_visual());
|
|
gtk_widget_set_default_colormap(gtk_preview_get_cmap());
|
|
|
|
dm->interface->dialog = gtk_dialog_new();
|
|
gtk_window_set_title(GTK_WINDOW(dm->interface->dialog), PLUG_IN_TITLE);
|
|
gtk_window_position(GTK_WINDOW(dm->interface->dialog), GTK_WIN_POS_MOUSE);
|
|
gtk_container_border_width(GTK_CONTAINER(dm->interface->dialog), 0);
|
|
gtk_signal_connect(GTK_OBJECT(dm->interface->dialog), "destroy",
|
|
(GtkSignalFunc)dialogCloseCallback, NULL);
|
|
|
|
/* topTable */
|
|
topTable = gtk_table_new(3, 3, FALSE);
|
|
gtk_container_border_width(GTK_CONTAINER(topTable), 6);
|
|
gtk_table_set_row_spacings(GTK_TABLE(topTable), 4);
|
|
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dm->interface->dialog)->vbox), topTable,
|
|
FALSE, FALSE, 0);
|
|
gtk_widget_show(topTable);
|
|
|
|
/* Preview */
|
|
previewFrame = gtk_frame_new(NULL);
|
|
gtk_frame_set_shadow_type(GTK_FRAME(previewFrame), GTK_SHADOW_IN);
|
|
gtk_table_attach(GTK_TABLE(topTable), previewFrame, 1, 2, 0, 1, 0, 0, 0, 0);
|
|
gtk_widget_show(previewFrame);
|
|
|
|
dm->interface->previewWidth = MIN(dm->selectionWidth, PREVIEW_SIZE);
|
|
dm->interface->previewHeight = MIN(dm->selectionHeight, PREVIEW_SIZE);
|
|
dm->interface->preview = gtk_preview_new(GTK_PREVIEW_COLOR);
|
|
gtk_preview_size(GTK_PREVIEW(dm->interface->preview),
|
|
dm->interface->previewWidth,
|
|
dm->interface->previewHeight);
|
|
gtk_container_add(GTK_CONTAINER(previewFrame), dm->interface->preview);
|
|
gtk_widget_show(dm->interface->preview);
|
|
|
|
DepthMerge_buildPreviewSourceImage(dm);
|
|
|
|
/* Source and Depth Map selection */
|
|
sourceTable = gtk_table_new(2, 4, FALSE);
|
|
gtk_container_border_width(GTK_CONTAINER(sourceTable), 2);
|
|
gtk_table_attach(GTK_TABLE(topTable), sourceTable, 0, 3, 1, 2,
|
|
GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
|
|
gtk_widget_show(sourceTable);
|
|
|
|
tempLabel = gtk_label_new("Source 1");
|
|
gtk_misc_set_alignment(GTK_MISC(tempLabel), 0.0, 0.5);
|
|
gtk_table_attach(GTK_TABLE(sourceTable), tempLabel, 0, 1, 0, 1,
|
|
GTK_FILL, GTK_FILL, 4, 0);
|
|
gtk_widget_show(tempLabel);
|
|
tempOptionMenu = gtk_option_menu_new();
|
|
gtk_table_attach(GTK_TABLE(sourceTable), tempOptionMenu, 1, 2, 0, 1,
|
|
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
|
|
gtk_widget_show(tempOptionMenu);
|
|
tempMenu = gimp_drawable_menu_new(
|
|
constraintResultSizeAndResultColorOrGray,
|
|
dialogSource1ChangedCallback,
|
|
dm,
|
|
dm->params.source1);
|
|
gtk_option_menu_set_menu(GTK_OPTION_MENU(tempOptionMenu), tempMenu);
|
|
gtk_widget_show(tempOptionMenu);
|
|
|
|
tempLabel = gtk_label_new("Depth Map");
|
|
gtk_misc_set_alignment(GTK_MISC(tempLabel), 0.0, 0.5);
|
|
gtk_table_attach(GTK_TABLE(sourceTable), tempLabel, 2, 3, 0, 1,
|
|
GTK_FILL, GTK_FILL, 4, 0);
|
|
gtk_widget_show(tempLabel);
|
|
tempOptionMenu = gtk_option_menu_new();
|
|
gtk_table_attach(GTK_TABLE(sourceTable), tempOptionMenu, 3, 4, 0, 1,
|
|
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
|
|
gtk_widget_show(tempOptionMenu);
|
|
tempMenu = gimp_drawable_menu_new(
|
|
constraintResultSizeAndResultColorOrGray,
|
|
dialogDepthMap1ChangedCallback,
|
|
dm,
|
|
dm->params.depthMap1);
|
|
gtk_option_menu_set_menu(GTK_OPTION_MENU(tempOptionMenu), tempMenu);
|
|
gtk_widget_show(tempOptionMenu);
|
|
|
|
tempLabel = gtk_label_new("Source 2");
|
|
gtk_misc_set_alignment(GTK_MISC(tempLabel), 0.0, 0.5);
|
|
gtk_table_attach(GTK_TABLE(sourceTable), tempLabel, 0, 1, 1, 2,
|
|
GTK_FILL, GTK_FILL, 4, 0);
|
|
gtk_widget_show(tempLabel);
|
|
tempOptionMenu = gtk_option_menu_new();
|
|
gtk_table_attach(GTK_TABLE(sourceTable), tempOptionMenu, 1, 2, 1, 2,
|
|
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
|
|
gtk_widget_show(tempOptionMenu);
|
|
tempMenu = gimp_drawable_menu_new(
|
|
constraintResultSizeAndResultColorOrGray,
|
|
dialogSource2ChangedCallback,
|
|
dm,
|
|
dm->params.source2);
|
|
gtk_option_menu_set_menu(GTK_OPTION_MENU(tempOptionMenu), tempMenu);
|
|
gtk_widget_show(tempOptionMenu);
|
|
|
|
tempLabel = gtk_label_new("Depth Map");
|
|
gtk_misc_set_alignment(GTK_MISC(tempLabel), 0.0, 0.5);
|
|
gtk_table_attach(GTK_TABLE(sourceTable), tempLabel, 2, 3, 1, 2,
|
|
GTK_FILL, GTK_FILL, 4, 0);
|
|
gtk_widget_show(tempLabel);
|
|
tempOptionMenu = gtk_option_menu_new();
|
|
gtk_table_attach(GTK_TABLE(sourceTable), tempOptionMenu, 3, 4, 1, 2,
|
|
GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
|
|
gtk_widget_show(tempOptionMenu);
|
|
tempMenu = gimp_drawable_menu_new(
|
|
constraintResultSizeAndResultColorOrGray,
|
|
dialogDepthMap2ChangedCallback,
|
|
dm,
|
|
dm->params.depthMap2);
|
|
gtk_option_menu_set_menu(GTK_OPTION_MENU(tempOptionMenu), tempMenu);
|
|
gtk_widget_show(tempOptionMenu);
|
|
|
|
/* Numeric parameters */
|
|
numericParameterTable = gtk_table_new(4, 3, FALSE);
|
|
gtk_container_border_width(GTK_CONTAINER(numericParameterTable), 2);
|
|
gtk_table_attach(GTK_TABLE(topTable), numericParameterTable, 0, 3, 2, 3,
|
|
GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
|
|
gtk_widget_show(numericParameterTable);
|
|
|
|
dm->interface->overlapValueEdit =
|
|
DepthMerge_createValueEdit(dm, "Overlap",
|
|
GTK_TABLE(numericParameterTable), 0, 0,
|
|
&(dm->params.overlap),
|
|
0, 2, 0.001);
|
|
dm->interface->offsetValueEdit =
|
|
DepthMerge_createValueEdit(dm, "Offset",
|
|
GTK_TABLE(numericParameterTable), 1, 0,
|
|
&(dm->params.offset),
|
|
-1, 1, 0.001);
|
|
dm->interface->scale1ValueEdit =
|
|
DepthMerge_createValueEdit(dm, "Scale 1",
|
|
GTK_TABLE(numericParameterTable), 2, 0,
|
|
&(dm->params.scale1),
|
|
-1, 1, 0.001);
|
|
dm->interface->scale2ValueEdit =
|
|
DepthMerge_createValueEdit(dm, "Scale 2",
|
|
GTK_TABLE(numericParameterTable), 3, 0,
|
|
&(dm->params.scale2),
|
|
-1, 1, 0.001);
|
|
|
|
/* Buttons */
|
|
|
|
gtk_container_border_width(GTK_CONTAINER(GTK_DIALOG(dm->interface->dialog)->action_area), 6);
|
|
tempButton = gtk_button_new_with_label("OK");
|
|
GTK_WIDGET_SET_FLAGS(tempButton, GTK_CAN_DEFAULT);
|
|
gtk_signal_connect(GTK_OBJECT(tempButton), "clicked",
|
|
(GtkSignalFunc)dialogOkCallback,
|
|
dm);
|
|
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dm->interface->dialog)->action_area),
|
|
tempButton, TRUE, TRUE, 0);
|
|
gtk_widget_grab_default(tempButton);
|
|
gtk_widget_show(tempButton);
|
|
|
|
tempButton = gtk_button_new_with_label("Cancel");
|
|
GTK_WIDGET_SET_FLAGS(tempButton, GTK_CAN_DEFAULT);
|
|
gtk_signal_connect(GTK_OBJECT(tempButton), "clicked",
|
|
(GtkSignalFunc)dialogCancelCallback,
|
|
dm);
|
|
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dm->interface->dialog)->action_area),
|
|
tempButton, TRUE, TRUE, 0);
|
|
gtk_widget_show(tempButton);
|
|
|
|
/* Done */
|
|
|
|
dm->interface->active = TRUE;
|
|
gtk_widget_show(dm->interface->dialog);
|
|
DepthMerge_updatePreview(dm);
|
|
|
|
gtk_main();
|
|
gdk_flush();
|
|
|
|
return(dm->interface->run);
|
|
}
|
|
|
|
|
|
DepthMergeValueEdit *DepthMerge_createValueEdit(DepthMerge *dm,
|
|
char *title, GtkTable *table,
|
|
int row, int startCol,
|
|
float *value,
|
|
float min, float max,
|
|
float step) {
|
|
GtkWidget *label;
|
|
GtkWidget *scale;
|
|
GtkWidget *entry;
|
|
GtkObject *scaleData;
|
|
DepthMergeValueEdit *valueEdit;
|
|
char buf[256];
|
|
|
|
valueEdit = (DepthMergeValueEdit *)g_malloc(sizeof(DepthMergeValueEdit));
|
|
valueEdit->dm = dm;
|
|
valueEdit->value = value;
|
|
|
|
*(valueEdit->value) = CLAMP(*(valueEdit->value), min, max);
|
|
|
|
label = gtk_label_new(title);
|
|
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 1.0);
|
|
gtk_table_attach(table, label, startCol, startCol+1, row, row+1,
|
|
GTK_FILL, GTK_FILL, 4, 0);
|
|
gtk_widget_show(label);
|
|
|
|
scaleData = gtk_adjustment_new(*value,
|
|
min, max,
|
|
step, step,
|
|
0.0);
|
|
gtk_signal_connect(GTK_OBJECT(scaleData), "value_changed",
|
|
(GtkSignalFunc)dialogValueScaleUpdateCallback,
|
|
valueEdit);
|
|
scale = gtk_hscale_new(GTK_ADJUSTMENT(scaleData));
|
|
gtk_widget_set_usize(scale, SCALE_WIDTH, 0);
|
|
gtk_table_attach(table, scale, startCol+1, startCol+2, row, row+1,
|
|
GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
|
|
gtk_scale_set_draw_value(GTK_SCALE(scale), FALSE);
|
|
gtk_scale_set_digits(GTK_SCALE(scale), 3);
|
|
gtk_range_set_update_policy(GTK_RANGE(scale), GTK_UPDATE_CONTINUOUS);
|
|
gtk_widget_show(scale);
|
|
|
|
entry = gtk_entry_new();
|
|
gtk_widget_set_usize(entry, ENTRY_WIDTH, 0);
|
|
sprintf(buf, "%0.3f", (float)*value);
|
|
gtk_entry_set_text(GTK_ENTRY(entry), buf);
|
|
gtk_signal_connect(GTK_OBJECT(entry), "changed",
|
|
(GtkSignalFunc) dialogValueEntryUpdateCallback,
|
|
valueEdit);
|
|
gtk_table_attach(GTK_TABLE(table), entry, startCol+2, startCol+3, row, row+1,
|
|
GTK_FILL, GTK_FILL, 4, 0);
|
|
gtk_widget_show(entry);
|
|
|
|
valueEdit->scaleData = GTK_ADJUSTMENT(scaleData);
|
|
valueEdit->entry = entry;
|
|
valueEdit->min = min;
|
|
valueEdit->max = max;
|
|
|
|
return(valueEdit);
|
|
}
|
|
|
|
|
|
void DepthMerge_buildPreviewSourceImage(DepthMerge *dm) {
|
|
int x;
|
|
|
|
dm->interface->checkRow0 = (guchar *)g_malloc(dm->interface->previewWidth *
|
|
sizeof(guchar));
|
|
dm->interface->checkRow1 = (guchar *)g_malloc(dm->interface->previewWidth *
|
|
sizeof(guchar));
|
|
|
|
for (x = 0; x < dm->interface->previewWidth; x++) {
|
|
if ((x / CHECK_SIZE) & 1) {
|
|
dm->interface->checkRow0[x] = CHECK_DARK;
|
|
dm->interface->checkRow1[x] = CHECK_LIGHT;
|
|
} else {
|
|
dm->interface->checkRow0[x] = CHECK_LIGHT;
|
|
dm->interface->checkRow1[x] = CHECK_DARK;
|
|
}
|
|
}
|
|
|
|
dm->interface->previewSource1 =
|
|
(guchar *)g_malloc(dm->interface->previewWidth *
|
|
dm->interface->previewHeight * 4);
|
|
util_fillReducedBuffer(dm->interface->previewSource1,
|
|
dm->interface->previewWidth,
|
|
dm->interface->previewHeight,
|
|
4, TRUE,
|
|
dm->source1Drawable,
|
|
dm->selectionX0, dm->selectionY0,
|
|
dm->selectionWidth, dm->selectionHeight);
|
|
dm->interface->previewSource2 =
|
|
(guchar *)g_malloc(dm->interface->previewWidth *
|
|
dm->interface->previewHeight * 4);
|
|
util_fillReducedBuffer(dm->interface->previewSource2,
|
|
dm->interface->previewWidth,
|
|
dm->interface->previewHeight,
|
|
4, TRUE,
|
|
dm->source2Drawable,
|
|
dm->selectionX0, dm->selectionY0,
|
|
dm->selectionWidth, dm->selectionHeight);
|
|
dm->interface->previewDepthMap1 =
|
|
(guchar *)g_malloc(dm->interface->previewWidth *
|
|
dm->interface->previewHeight * 1);
|
|
util_fillReducedBuffer(dm->interface->previewDepthMap1,
|
|
dm->interface->previewWidth,
|
|
dm->interface->previewHeight,
|
|
1, FALSE,
|
|
dm->depthMap1Drawable,
|
|
dm->selectionX0, dm->selectionY0,
|
|
dm->selectionWidth, dm->selectionHeight);
|
|
dm->interface->previewDepthMap2 =
|
|
(guchar *)g_malloc(dm->interface->previewWidth *
|
|
dm->interface->previewHeight * 1);
|
|
util_fillReducedBuffer(dm->interface->previewDepthMap2,
|
|
dm->interface->previewWidth,
|
|
dm->interface->previewHeight,
|
|
1, FALSE,
|
|
dm->depthMap2Drawable,
|
|
dm->selectionX0, dm->selectionY0,
|
|
dm->selectionWidth, dm->selectionHeight);
|
|
}
|
|
|
|
void DepthMerge_updatePreview(DepthMerge *dm) {
|
|
int x, y;
|
|
guchar *source1Row, *source2Row, *depthMap1Row, *depthMap2Row,
|
|
*resultRowRGBA, *resultRow,
|
|
*checkRow;
|
|
|
|
if (!dm->interface->active) return;
|
|
|
|
resultRowRGBA = (guchar *)g_malloc(dm->interface->previewWidth * 4);
|
|
resultRow = (guchar *)g_malloc(dm->interface->previewWidth * 3);
|
|
|
|
for (y = 0; y < dm->interface->previewHeight; y++) {
|
|
checkRow = ((y/CHECK_SIZE)&1) ?
|
|
dm->interface->checkRow1 :
|
|
dm->interface->checkRow0;
|
|
|
|
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,
|
|
resultRowRGBA,
|
|
dm->interface->previewWidth);
|
|
for (x = 0; x < dm->interface->previewWidth; x++) {
|
|
resultRow[x*3 ] =
|
|
((int)( resultRowRGBA[x*4+3])*(int)resultRowRGBA[x*4 ] +
|
|
(int)(255 - resultRowRGBA[x*4+3])*(int)checkRow[x] ) / 255;
|
|
resultRow[x*3+1] =
|
|
((int)( resultRowRGBA[x*4+3])*(int)resultRowRGBA[x*4+1] +
|
|
(int)(255 - resultRowRGBA[x*4+3])*(int)checkRow[x] ) / 255;
|
|
resultRow[x*3+2] =
|
|
((int)( resultRowRGBA[x*4+3])*(int)resultRowRGBA[x*4+2] +
|
|
(int)(255 - resultRowRGBA[x*4+3])*(int)checkRow[x] ) / 255;
|
|
}
|
|
gtk_preview_draw_row(GTK_PREVIEW(dm->interface->preview), resultRow, 0, y,
|
|
dm->interface->previewWidth);
|
|
}
|
|
|
|
g_free(resultRowRGBA);
|
|
g_free(resultRow);
|
|
|
|
gtk_widget_draw(dm->interface->preview, NULL);
|
|
gdk_flush();
|
|
}
|
|
|
|
|
|
/* ----- Callbacks ----- */
|
|
|
|
|
|
gint constraintResultSizeAndResultColorOrGray(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_color(drawableId) &&
|
|
(gimp_drawable_color(dm->params.result))) ||
|
|
gimp_drawable_gray( drawableId))));
|
|
}
|
|
|
|
gint constraintResultSizeAndGray(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_gray( drawableId))));
|
|
}
|
|
|
|
void dialogOkCallback(GtkWidget *widget, gpointer data) {
|
|
DepthMerge *dm = (DepthMerge *)data;
|
|
dm->interface->run = TRUE;
|
|
gtk_widget_destroy(dm->interface->dialog);
|
|
}
|
|
|
|
void dialogCancelCallback(GtkWidget *widget, gpointer data) {
|
|
DepthMerge *dm = (DepthMerge *)data;
|
|
dm->interface->run = FALSE;
|
|
gtk_widget_destroy(dm->interface->dialog);
|
|
}
|
|
|
|
void dialogCloseCallback(GtkWidget *widget, gpointer data) {
|
|
gtk_main_quit();
|
|
}
|
|
|
|
|
|
void dialogSource1ChangedCallback(gint32 id, gpointer data) {
|
|
DepthMerge *dm = (DepthMerge *)data;
|
|
|
|
if (dm->source1Drawable != NULL)
|
|
gimp_drawable_detach(dm->source1Drawable );
|
|
dm->params.source1 = id;
|
|
dm->source1Drawable = (dm->params.source1 == -1) ? NULL :
|
|
gimp_drawable_get(dm->params.source1 );
|
|
|
|
util_fillReducedBuffer(dm->interface->previewSource1,
|
|
dm->interface->previewWidth,
|
|
dm->interface->previewHeight,
|
|
4, TRUE,
|
|
dm->source1Drawable,
|
|
dm->selectionX0, dm->selectionY0,
|
|
dm->selectionWidth, dm->selectionHeight);
|
|
|
|
DepthMerge_updatePreview(dm);
|
|
}
|
|
void dialogSource2ChangedCallback(gint32 id, gpointer data) {
|
|
DepthMerge *dm = (DepthMerge *)data;
|
|
|
|
if (dm->source2Drawable != NULL)
|
|
gimp_drawable_detach(dm->source2Drawable );
|
|
dm->params.source2 = id;
|
|
dm->source2Drawable = (dm->params.source2 == -1) ? NULL :
|
|
gimp_drawable_get(dm->params.source2 );
|
|
|
|
util_fillReducedBuffer(dm->interface->previewSource2,
|
|
dm->interface->previewWidth,
|
|
dm->interface->previewHeight,
|
|
4, TRUE,
|
|
dm->source2Drawable,
|
|
dm->selectionX0, dm->selectionY0,
|
|
dm->selectionWidth, dm->selectionHeight);
|
|
|
|
DepthMerge_updatePreview(dm);
|
|
}
|
|
void dialogDepthMap1ChangedCallback(gint32 id, gpointer data) {
|
|
DepthMerge *dm = (DepthMerge *)data;
|
|
|
|
if (dm->depthMap1Drawable != NULL)
|
|
gimp_drawable_detach(dm->depthMap1Drawable);
|
|
dm->params.depthMap1 = id;
|
|
dm->depthMap1Drawable = (dm->params.depthMap1 == -1) ? NULL :
|
|
gimp_drawable_get(dm->params.depthMap1);
|
|
|
|
util_fillReducedBuffer(dm->interface->previewDepthMap1,
|
|
dm->interface->previewWidth,
|
|
dm->interface->previewHeight,
|
|
1, FALSE,
|
|
dm->depthMap1Drawable,
|
|
dm->selectionX0, dm->selectionY0,
|
|
dm->selectionWidth, dm->selectionHeight);
|
|
|
|
DepthMerge_updatePreview(dm);
|
|
}
|
|
void dialogDepthMap2ChangedCallback(gint32 id, gpointer data) {
|
|
DepthMerge *dm = (DepthMerge *)data;
|
|
|
|
if (dm->depthMap2Drawable != NULL)
|
|
gimp_drawable_detach(dm->depthMap2Drawable);
|
|
dm->params.depthMap2 = id;
|
|
dm->depthMap2Drawable = (dm->params.depthMap2 == -1) ? NULL :
|
|
gimp_drawable_get(dm->params.depthMap2);
|
|
|
|
util_fillReducedBuffer(dm->interface->previewDepthMap2,
|
|
dm->interface->previewWidth,
|
|
dm->interface->previewHeight,
|
|
1, FALSE,
|
|
dm->depthMap2Drawable,
|
|
dm->selectionX0, dm->selectionY0,
|
|
dm->selectionWidth, dm->selectionHeight);
|
|
|
|
DepthMerge_updatePreview(dm);
|
|
}
|
|
|
|
void dialogValueScaleUpdateCallback(GtkAdjustment *adjustment, gpointer data) {
|
|
DepthMergeValueEdit *valueEdit = (DepthMergeValueEdit *)data;
|
|
char buf[256];
|
|
|
|
if (*(valueEdit->value) != adjustment->value) {
|
|
*(valueEdit->value) = adjustment->value;
|
|
|
|
sprintf(buf, "%0.3f", (float)(adjustment->value));
|
|
|
|
gtk_signal_handler_block_by_data(GTK_OBJECT(valueEdit->entry), data);
|
|
gtk_entry_set_text(GTK_ENTRY(valueEdit->entry), buf);
|
|
gtk_signal_handler_unblock_by_data(GTK_OBJECT(valueEdit->entry), data);
|
|
|
|
DepthMerge_updatePreview(valueEdit->dm);
|
|
}
|
|
}
|
|
|
|
void dialogValueEntryUpdateCallback(GtkWidget *widget, gpointer data) {
|
|
DepthMergeValueEdit *valueEdit = (DepthMergeValueEdit *)data;
|
|
float newValue;
|
|
|
|
newValue = atof(gtk_entry_get_text(GTK_ENTRY(widget)));
|
|
|
|
if ((*(valueEdit->value) != newValue) &&
|
|
(newValue >= valueEdit->min) &&
|
|
(newValue <= valueEdit->max)) {
|
|
valueEdit->scaleData->value = newValue;
|
|
|
|
gtk_signal_emit_by_name(GTK_OBJECT(valueEdit->scaleData),
|
|
"value_changed");
|
|
|
|
DepthMerge_updatePreview(valueEdit->dm);
|
|
}
|
|
}
|
|
|
|
|
|
/* ----- Utility routines ----- */
|
|
|
|
void util_fillReducedBuffer(guchar *dest, gint destWidth, gint destHeight,
|
|
gint destBPP, gint destHasAlpha,
|
|
GDrawable *sourceDrawable,
|
|
gint x0, gint y0,
|
|
gint sourceWidth, gint sourceHeight) {
|
|
GPixelRgn rgn;
|
|
guchar *rowBuffer, *reducedRowBuffer;
|
|
int x, y, xPrime, yPrime, sourceHasAlpha;
|
|
|
|
if ((sourceDrawable == NULL) || (sourceWidth == 0) || (sourceHeight == 0)) {
|
|
for (x = 0; x < destWidth*destHeight*destBPP; x++)
|
|
dest[x] = 0;
|
|
return;
|
|
}
|
|
|
|
rowBuffer = (guchar *)g_malloc(sourceWidth * sourceDrawable->bpp);
|
|
reducedRowBuffer = (guchar *)g_malloc(destWidth * sourceDrawable->bpp);
|
|
gimp_pixel_rgn_init(&rgn, sourceDrawable, x0, y0, sourceWidth, sourceHeight,
|
|
FALSE, FALSE);
|
|
sourceHasAlpha = gimp_drawable_has_alpha(sourceDrawable->id);
|
|
|
|
for (y = 0; y < destHeight; y++) {
|
|
yPrime = y*(sourceHeight-1)/(destHeight-1);
|
|
gimp_pixel_rgn_get_row(&rgn, rowBuffer, x0, yPrime+y0,
|
|
sourceWidth);
|
|
for (x = 0; x < destWidth; x++) {
|
|
xPrime = x*(sourceWidth-1)/(destWidth-1);
|
|
memcpy(&(reducedRowBuffer[x *sourceDrawable->bpp]),
|
|
&(rowBuffer[ xPrime*sourceDrawable->bpp]),
|
|
sourceDrawable->bpp);
|
|
}
|
|
util_convertColorspace(&(dest[y*destWidth*destBPP]), destBPP, destHasAlpha,
|
|
reducedRowBuffer, sourceDrawable->bpp, sourceHasAlpha,
|
|
destWidth);
|
|
}
|
|
|
|
g_free(rowBuffer);
|
|
g_free(reducedRowBuffer);
|
|
}
|
|
|
|
|
|
/* Utterly pathetic kludge to convert between color spaces;
|
|
likes gray and rgb best, of course. Others will be creatively mutilated,
|
|
and even rgb->gray is pretty bad */
|
|
void util_convertColorspace(guchar *dest,
|
|
gint destBPP, gint destHasAlpha,
|
|
guchar *source,
|
|
gint sourceBPP, gint sourceHasAlpha,
|
|
gint length) {
|
|
int i, j, accum;
|
|
int sourceColorBPP = sourceHasAlpha ? (sourceBPP-1) : sourceBPP;
|
|
int destColorBPP = destHasAlpha ? (destBPP -1) : destBPP;
|
|
|
|
if (((sourceColorBPP != 1) && (sourceColorBPP != 3)) ||
|
|
((destColorBPP != 1) && (destColorBPP != 3)))
|
|
fprintf(stderr, "Warning: I don't _like_ this color space. This is a suggestion, not a threat.\n");
|
|
|
|
if ((sourceColorBPP == destColorBPP) &&
|
|
(sourceBPP == destBPP )) {
|
|
memcpy(dest, source, length*sourceBPP);
|
|
return;
|
|
}
|
|
|
|
if (sourceColorBPP == 1) {
|
|
/* Duplicate single "gray" source byte across all dest bytes */
|
|
for (i = 0; i < length; i++) {
|
|
for (j = 0; j < destColorBPP; j++)
|
|
dest[i*destBPP + j] = source[i*sourceBPP];
|
|
}
|
|
} else if (destColorBPP == 1) {
|
|
/* Average all source bytes into single "gray" dest byte */
|
|
for (i = 0; i < length; i++) {
|
|
accum = 0;
|
|
for (j = 0; j < sourceColorBPP; j++)
|
|
accum += source[i*sourceBPP + j];
|
|
dest[i*destBPP] = accum/sourceColorBPP;
|
|
}
|
|
} else if (destColorBPP < sourceColorBPP) {
|
|
/* Copy as many corresponding bytes from source to dest as will fit */
|
|
for (i = 0; i < length; i++) {
|
|
for (j = 0; j < destColorBPP; j++)
|
|
dest[i*destBPP + j] = source[i*sourceBPP + j];
|
|
}
|
|
} else /* destColorBPP > sourceColorBPP */ {
|
|
/* Fill extra dest bytes with zero */
|
|
for (i = 0; i < length; i++) {
|
|
for (j = 0; j < sourceColorBPP; j++)
|
|
dest[i*destBPP + j] = source[i*sourceBPP + j];
|
|
for ( ; j < destColorBPP; j++)
|
|
dest[i*destBPP + j] = 0;
|
|
}
|
|
}
|
|
|
|
if (destHasAlpha) {
|
|
if (sourceHasAlpha)
|
|
for (i = 0; i < length; i++)
|
|
dest[i*destBPP + destColorBPP] =
|
|
source[i*sourceBPP + sourceColorBPP];
|
|
else
|
|
for (i = 0; i < length; i++)
|
|
dest[i*destBPP + destColorBPP] =
|
|
255;
|
|
}
|
|
}
|