Hi there,
for people who might be interested in writing Cuda enabled Gimp plug-in, here’s what I’ve been able to do as a base. Hope it might be useful.
Being new to Gimp plug-in and Cuda, improvements/comments are welcome (there may be mistakes for sure ;-) ).
It is divided into 2 files : gimpcuda.cu and gimpcuda_kernel.cu
The first retrieves image raw data from Gimp, calls the second, and puts back the computed values to Gimp.
To compile, I used the following command :
nvcc -o cudatest --host-compilation C pkg-config --cflags --libs gimp-2.0
gimpcuda.cu gimpcuda_kernel.cu
(the plug-in just inverts the current image)
gimpcuda.cu
#include <libgimp/gimp.h>
void kernel_render(unsigned char *buffer,int width,int height,int channels);
static void query(void) {
static GimpParamDef args[]={
{GIMP_PDB_INT32 ,"run-mode","Run mode" },
{GIMP_PDB_IMAGE ,"image" ,"Input image" },
{GIMP_PDB_DRAWABLE,"drawable","Input drawable"}
};
gimp_install_procedure(
"plug-in-cuda-test",
"Plug-in test cuda de Fred",
"Teste l'intégration de Cuda dans un plug-in Gimp",
"Frédéric BISSON",
"Copyright Frédéric BISSON",
"2008",
"_Cuda test...",
"RGB",
GIMP_PLUGIN,
G_N_ELEMENTS (args), 0,
args, NULL
);
gimp_plugin_menu_register("plug-in-cuda-test","<Image>/Filters/Misc");
}
static void render(GimpDrawable *drawable) {
gint channels;
gint x1,y1,x2,y2;
GimpPixelRgn src,dst;
guchar *buffer;
gint width,height;
gimp_drawable_mask_bounds(drawable->drawable_id,&x1,&y1,&x2,&y2);
channels=gimp_drawable_bpp(drawable->drawable_id);
width =x2-x1;
height=y2-y1;
gimp_pixel_rgn_init(&src,drawable,x1,y1,width,height,FALSE,FALSE);
gimp_pixel_rgn_init(&dst,drawable,x1,y1,width,height,TRUE ,TRUE );
buffer=g_new(guchar,channels*width*height);
gimp_pixel_rgn_get_rect(&src,buffer,x1,y1,width,height);
kernel_render(buffer,width,height,channels);
gimp_pixel_rgn_set_rect(&dst,buffer,x1,y1,width,height);
g_free(buffer);
gimp_drawable_flush(drawable);
gimp_drawable_merge_shadow(drawable->drawable_id,TRUE);
gimp_drawable_update(drawable->drawable_id,x1,y1,width,height);
}
static void run(const gchar *name,gint nparams,const GimpParam *param,gint *nreturn_vals,GimpParam **return_vals) {
static GimpParam values[1];
GimpPDBStatusType status = GIMP_PDB_SUCCESS;
GimpRunMode run_mode;
GimpDrawable *drawable;
// Setting mandatory output values
*nreturn_vals=1;
*return_vals =values;
values[0].type =GIMP_PDB_STATUS;
values[0].data.d_status=status;
// Getting run_mode - we won't display a dialog if we are in NONINTERACTIVE mode
run_mode=(GimpRunMode)param[0].data.d_int32;
if(run_mode!=GIMP_RUN_NONINTERACTIVE) {
g_message("Cuda test in progress...\n");
}
// Get specified drawable
drawable=gimp_drawable_get(param[2].data.d_drawable);
render(drawable);
gimp_displays_flush();
gimp_drawable_detach(drawable);
}
GimpPlugInInfo PLUG_IN_INFO={
NULL,
NULL,
query,
run
};
MAIN()
gimpcuda_kernel.cu
#include <stdio.h>
__global__ void doProcessPicture(unsigned char *src,unsigned char *dst,int width,int height,int channels) {
int i,j;
int base=threadIdx.x*width*channels*gridDim.x;
int offset;
for(j=0;j<gridDim.x;j++) {
for(i=0;i<width*channels;i++) {
offset=base+i+j*width*channels;
dst[offset]=255-src[offset];
}
}
}
void kernel_render(unsigned char *buffer,int width,int height,int channels) {
unsigned char *devSrc;
unsigned char *devDst;
int bufferSize;
int nbIter;
int nbThread;
bufferSize=width*height*channels;
nbThread=192;
nbIter=height/nbThread;
// Allocate two buffers on the GPU
cudaMalloc((void**)&devSrc,bufferSize);
cudaMalloc((void**)&devDst,bufferSize);
// Copy our buffer into the GPU input buffer
cudaMemcpy(devSrc,buffer,bufferSize,cudaMemcpyHostToDevice);
// Run the GPU routine
doProcessPicture<<<nbIter,nbThread,0>>>(devSrc,devDst,width,height,channels);
// Retrieve the GPU output buffer into our buffer
cudaMemcpy(buffer,devDst,bufferSize,cudaMemcpyDeviceToHost);
// Free allocated GPU buffers
cudaFree(devSrc);
cudaFree(devDst);
}