gimp/plug-ins/common/winclipboard.c

652 lines
16 KiB
C

/*
* WinClipboard Win32 Windoze Copy&Paste Plug-in
* Copyright (C) 1999 Hans Breuer
* Hans Breuer, Hans@Breuer.org
* 08/07/99
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Based on (at least) the following plug-ins:
* Header
* WinSnap
*
* Any suggestions, bug-reports or patches are welcome.
*/
#include "config.h"
#include <windows.h>
#include <stdlib.h>
#include <libgimp/gimp.h>
#include "libgimp/stdplugins-intl.h"
/* History:
*
* 08/07/99 Implementation and release.
* 08/10/99 Big speed increase by using gimp_tile_cache_size()
* Thanks to Kevin Turner's documentation at:
* http://www.poboxes.com/kevint/gimp/doc/plugin-doc-2.1.html
*
* TODO (maybe):
*
* - Support for 4,2,1 bit bitmaps
* - Unsupported formats could be delegated to GIMP Loader (e.g. wmf)
* - ...
*/
/* How many steps the progress control should do
*/
#define PROGRESS_STEPS 25
#define StepProgress(one,all) \
(0 == (one % ((all / PROGRESS_STEPS)+1)))
/* FIXME: I'll use -1 as IMAGE_NONE. Is it correct ???
*/
#define IMAGE_NONE -1
/* Declare some local functions.
*/
static void query (void);
static void run (gchar *name,
gint nparams,
GimpParam *param,
gint *nreturn_vals,
GimpParam **return_vals);
/* Plugin function prototypes
*/
static int CB_CopyImage (gboolean interactive,
gint32 image_ID,
gint32 drawable_ID);
static int CB_PasteImage (gboolean interactive,
gint32 image_ID,
gint32 drawable_ID);
GimpPlugInInfo PLUG_IN_INFO =
{
NULL, /* init_proc */
NULL, /* quit_proc */
query, /* query_proc */
run, /* run_proc */
};
MAIN ()
static void
query ()
{
static GimpParamDef copy_args[] =
{
{ GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
{ GIMP_PDB_IMAGE, "image", "Input image" },
{ GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" }
};
static gint ncopy_args = sizeof (copy_args) / sizeof (copy_args[0]);
gimp_install_procedure ("plug_in_clipboard_copy",
"copy image to clipboard",
"Copies the active drawable to the clipboard.",
"Hans Breuer",
"Hans Breuer",
"1999",
N_("<Image>/Edit/Copy to Clipboard"),
"INDEXED*, RGB*",
GIMP_PLUGIN,
ncopy_args, 0,
copy_args, NULL);
gimp_install_procedure ("plug_in_clipboard_paste",
"paste image from clipboard",
"Paste image from clipboard into active image.",
"Hans Breuer",
"Hans Breuer",
"1999",
N_("<Image>/Edit/Paste from Clipboard"),
"INDEXED*, RGB*",
GIMP_PLUGIN,
ncopy_args, 0,
copy_args, NULL);
gimp_install_procedure ("extension_clipboard_paste",
"Get image from clipboard",
"Get an image from the Windows clipboard, creating a new image",
"Hans Breuer",
"Hans Breuer",
"1999",
N_("<Toolbox>/File/Acquire/From Clipboard"),
"",
GIMP_EXTENSION,
ncopy_args, 0,
copy_args, NULL);
}
static void
run (gchar *name,
gint nparams,
GimpParam *param,
gint *nreturn_vals,
GimpParam **return_vals)
{
static GimpParam values[2];
GimpRunModeType run_mode;
run_mode = param[0].data.d_int32;
*nreturn_vals = 1;
*return_vals = values;
values[0].type = GIMP_PDB_STATUS;
values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
INIT_I18N();
if (strcmp (name, "plug_in_clipboard_copy") == 0)
{
*nreturn_vals = 1;
if (CB_CopyImage (GIMP_RUN_INTERACTIVE==run_mode,
param[1].data.d_int32,
param[2].data.d_int32))
values[0].data.d_status = GIMP_PDB_SUCCESS;
else
values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
}
else if (strcmp (name, "plug_in_clipboard_paste") == 0)
{
*nreturn_vals = 1;
if (CB_PasteImage (GIMP_RUN_INTERACTIVE==run_mode,
param[1].data.d_int32,
param[2].data.d_int32))
values[0].data.d_status = GIMP_PDB_SUCCESS;
else
values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
}
else if (strcmp (name, "extension_clipboard_paste") == 0)
{
*nreturn_vals = 1;
if (CB_PasteImage (GIMP_RUN_INTERACTIVE==run_mode,
IMAGE_NONE,
IMAGE_NONE))
values[0].data.d_status = GIMP_PDB_SUCCESS;
else
values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
}
}
/* Plugin Function implementation
*/
static int
CB_CopyImage (gboolean interactive,
gint32 image_ID,
gint32 drawable_ID)
{
GimpDrawable *drawable;
GimpImageType drawable_type;
GimpPixelRgn pixel_rgn;
gchar* sStatus = NULL;
int nSizeDIB=0;
int nSizePal=0;
int nSizeLine=0; /* DIB lines are 32 bit aligned */
HANDLE hDIB;
BOOL bRet;
drawable = gimp_drawable_get (drawable_ID);
drawable_type = gimp_drawable_type (drawable_ID);
gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, drawable->width, drawable->height, FALSE, FALSE);
/* allocate room for DIB */
if (GIMP_INDEXED_IMAGE == drawable_type)
{
nSizeLine = ((drawable->width-1)/4+1)*4;
nSizeDIB = sizeof(RGBQUAD) * 256 /* always full color map size */
+ nSizeLine * drawable->height
+ sizeof (BITMAPINFOHEADER);
}
else
{
nSizeLine = ((drawable->width*3-1)/4+1)*4;
nSizeDIB = nSizeLine * drawable->height
+ sizeof (BITMAPINFOHEADER);
}
hDIB = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, nSizeDIB);
if (NULL == hDIB)
{
g_warning("Failed to allocate DIB");
bRet = FALSE;
}
/* fill header info */
if (bRet)
{
BITMAPINFOHEADER* pInfo;
bRet = FALSE;
pInfo = GlobalLock(hDIB);
if (pInfo)
{
pInfo->biSize = sizeof(BITMAPINFOHEADER);
pInfo->biWidth = drawable->width;
pInfo->biHeight = drawable->height;
pInfo->biPlanes = 1;
pInfo->biBitCount = (GIMP_INDEXED_IMAGE == drawable_type ? 8 : 24);
pInfo->biCompression = BI_RGB; /* none */
pInfo->biSizeImage = 0; /* not calculated/needed */
pInfo->biXPelsPerMeter =
pInfo->biYPelsPerMeter = 0;
/* color map size */
pInfo->biClrUsed = (GIMP_INDEXED_IMAGE == drawable_type ? 256 : 0);
pInfo->biClrImportant = 0; /* all */
GlobalUnlock(hDIB);
bRet = TRUE;
} /* (pInfo) */
else
g_warning("Failed to lock DIB Header");
}
/* fill color map */
if (bRet && (GIMP_INDEXED_IMAGE == drawable_type))
{
char* pBmp;
bRet = FALSE;
pBmp = GlobalLock(hDIB);
if (pBmp)
{
RGBQUAD* pPal;
int nColors;
unsigned char *cmap;
pPal = (RGBQUAD*)(pBmp + sizeof(BITMAPINFOHEADER));
nSizePal = sizeof(RGBQUAD) * 256;
/* get the gimp colormap */
cmap = gimp_image_get_cmap (image_ID, &nColors);
if (cmap)
{
int i;
for (i = 0; (i < 256) && (i < nColors); i++)
{
pPal[i].rgbReserved = 0; /* is this alpha? */
pPal[i].rgbRed = cmap[3*i];
pPal[i].rgbGreen = cmap[3*i+1];
pPal[i].rgbBlue = cmap[3*i+2];
}
g_free(cmap);
bRet = TRUE;
} /* (cmap) */
else
g_warning("Can't get color map");
GlobalUnlock(hDIB);
} /* (pBmp) */
else
g_warning("Failed to lock DIB Palette");
} /* indexed */
/* following the slow part ... */
if (interactive)
gimp_progress_init ( _("Copying ..."));
/* speed it up with: */
gimp_tile_cache_size( drawable->width * gimp_tile_height()
* drawable->bpp );
/* copy data to DIB */
if (bRet)
{
unsigned char* pData;
bRet = FALSE;
pData = GlobalLock(hDIB);
if (pData)
{
unsigned char* pLine;
/* calculate real offset */
pData += (sizeof(BITMAPINFOHEADER) + nSizePal);
pLine = g_new (guchar, drawable->width * drawable->bpp);
if (pLine)
{
if (GIMP_INDEXED_IMAGE == drawable_type)
{
int x, y;
for (y = 0; y < drawable->height; y++)
{
if ((interactive) && (StepProgress(y,drawable->height)))
gimp_progress_update((double)y / drawable->height);
gimp_pixel_rgn_get_row (&pixel_rgn, pLine, 0,
drawable->height-y-1, /* invert it */
drawable->width);
for (x = 0; x < drawable->width; x++)
pData[x+y*nSizeLine] = pLine[x*drawable->bpp];
}
}
else
{
int x, y;
for (y = 0; y < drawable->height; y++)
{
if ((interactive) && (StepProgress(y,drawable->height)))
gimp_progress_update((double)y / drawable->height);
gimp_pixel_rgn_get_row (&pixel_rgn, pLine, 0,
drawable->height-y-1, /* invert it */
drawable->width);
for (x = 0; x < drawable->width; x++)
{
/* RGBQUAD: blue, green, red, reserved */
pData[x*3+y*nSizeLine] = pLine[x*drawable->bpp+2]; /* blue */
pData[x*3+y*nSizeLine+1] = pLine[x*drawable->bpp+1]; /* green */
pData[x*3+y*nSizeLine+2] = pLine[x*drawable->bpp]; /* red */
/*pData[x+y*drawable->width*3+3] = 0;*/ /* reserved */
}
}
}
g_free(pLine);
bRet = TRUE;
} /* (pLine) */
else
g_warning("Failed to get line buffer");
GlobalUnlock(hDIB);
} /* (pData) */
else
g_warning("Failed to lock DIB Data");
} /* copy data to DIB */
/* copy DIB to ClipBoard */
if (bRet)
{
if (!OpenClipboard(NULL))
{
g_warning( "Cannot open the Clipboard!" );
bRet = FALSE;
}
else
{
if (bRet && !EmptyClipboard())
{
g_warning( "Cannot empty the Clipboard" );
bRet = FALSE;
}
if (bRet)
{
if (NULL != SetClipboardData(CF_DIB, hDIB))
hDIB = NULL; /* data now owned by clipboard */
else
g_warning ("Failed to set clipboard data ");
}
if (!CloseClipboard())
g_warning("Failed to close Clipboard");
}
}
/* done */
if (hDIB) GlobalFree(hDIB);
gimp_drawable_detach (drawable);
return bRet;
} /* CB_CopyImage */
static int
CB_PasteImage (gboolean interactive,
gint32 image_ID,
gint32 drawable_ID)
{
UINT fmt;
BOOL bRet=TRUE;
HANDLE hDIB;
gint32 nWidth = 0;
gint32 nHeight = 0;
gint32 nBitsPS = 0;
gint32 nColors = 0;
if (!OpenClipboard(NULL))
{
g_warning("Failed to open clipboard");
return FALSE;
}
fmt = EnumClipboardFormats(0);
while ((CF_BITMAP != fmt) && (CF_DIB != fmt) && (0 != fmt))
fmt = EnumClipboardFormats(fmt);
if (0 == fmt)
{
g_message("Unsupported format or Clipboard empty!");
bRet = FALSE;
}
/* there is something supported */
if (bRet)
{
hDIB = GetClipboardData(CF_DIB);
if (NULL == hDIB)
{
g_warning("Can't get Clipboard data");
bRet = FALSE;
}
}
/* read header */
if (bRet && hDIB)
{
BITMAPINFOHEADER* pInfo;
pInfo = GlobalLock(hDIB);
if (NULL == pInfo)
{
g_warning("Can't lock clipboard data!");
bRet = FALSE;
}
if ((bRet) &&
((pInfo->biSize != sizeof(BITMAPINFOHEADER)
|| (pInfo->biCompression != BI_RGB))))
{
g_warning("Unupported bitmap format!");
bRet = FALSE;
}
if (bRet && pInfo)
{
nWidth = pInfo->biWidth;
nHeight = pInfo->biHeight;
nBitsPS = pInfo->biBitCount;
nColors = pInfo->biClrUsed;
}
GlobalUnlock(hDIB);
}
if ((0 != nWidth) && (0 != nHeight))
{
GimpDrawable* drawable;
GimpPixelRgn pixel_rgn;
char* pData;
GimpParam *params;
gint retval;
gboolean bIsNewImage = TRUE;
gint oldBPP=0;
/* Check if clipboard data and existing image are compatible */
if (IMAGE_NONE != drawable_ID)
{
drawable = gimp_drawable_get(drawable_ID);
oldBPP = drawable->bpp;
gimp_drawable_detach(drawable);
}
if ((IMAGE_NONE == image_ID)
|| (3 != oldBPP) || (24 != nBitsPS))
{
/* create new image */
image_ID = gimp_image_new (nWidth, nHeight, nBitsPS <= 8 ? GIMP_INDEXED : GIMP_RGB);
gimp_image_undo_disable(image_ID);
drawable_ID = gimp_layer_new (image_ID, _("Background"), nWidth, nHeight,
nBitsPS <= 8 ? GIMP_INDEXED_IMAGE : GIMP_RGB_IMAGE,
100, GIMP_NORMAL_MODE);
}
else
{
/* ??? gimp_convert_rgb(image_ID);
*/
drawable_ID = gimp_layer_new (image_ID, _("Pasted"), nWidth, nHeight,
nBitsPS <= 8 ? GIMP_INDEXED_IMAGE : GIMP_RGB_IMAGE,
100, GIMP_NORMAL_MODE);
bIsNewImage = FALSE;
}
gimp_image_add_layer(image_ID,drawable_ID,-1);
drawable = gimp_drawable_get(drawable_ID);
gimp_pixel_rgn_init(&pixel_rgn, drawable, 0, 0, drawable->width, drawable->height, FALSE, FALSE);
/* following the slow part ... */
if (interactive)
gimp_progress_init ( _("Pasting..."));
pData = GlobalLock(hDIB);
if (pData)
{
guchar* pLine;
RGBQUAD* pPal;
int nSizeLine=0; /* DIB lines are 32 bit aligned */
nSizeLine = ((nWidth*(nBitsPS/8)-1)/4+1)*4;
/* adjust pointer */
pPal = (RGBQUAD*)(pData + sizeof(BITMAPINFOHEADER));
pData = (guchar*)pPal + sizeof(RGBQUAD) * nColors;
/* create palette */
if (0 != nColors)
{
int c;
guchar* cmap;
cmap = g_new(guchar, nColors*3);
if (cmap)
{
for (c = 0; c < nColors; c++)
{
cmap[c*3] = pPal[c].rgbRed;
cmap[c*3+1] = pPal[c].rgbGreen;
cmap[c*3+2] = pPal[c].rgbBlue;
}
gimp_image_set_cmap(image_ID, cmap, nColors);
g_free(cmap);
}
}
/* speed it up with: */
gimp_tile_cache_size( drawable->width * gimp_tile_height()
* drawable->bpp );
/* change data format and copy data */
if (24 == nBitsPS)
{
pLine = g_new(guchar, drawable->width*drawable->bpp);
if (pLine)
{
int y;
for (y = 0; y < drawable->height; y++)
{
int x;
if ((interactive) && (StepProgress(y,drawable->height)))
gimp_progress_update((double)y / drawable->height);
for (x = 0; x < drawable->width; x++)
{
pLine[x*drawable->bpp] = pData[y*nSizeLine+x*3+2];
pLine[x*drawable->bpp+1] = pData[y*nSizeLine+x*3+1];
pLine[x*drawable->bpp+2] = pData[y*nSizeLine+x*3];
}
/* copy data to GIMP */
gimp_pixel_rgn_set_rect(&pixel_rgn, pLine, 0, drawable->height-1-y, drawable->width, 1);
}
g_free(pLine);
}
}
else if (8 == nBitsPS)
{
int y;
/* copy line by line */
for (y = 0; y < drawable->height; y++)
{
if ((interactive) && (StepProgress(y,drawable->height)))
gimp_progress_update((double)y / drawable->height);
pLine = pData + y*nSizeLine; /* adjust pointer */
gimp_pixel_rgn_set_row(&pixel_rgn, pLine, 0, drawable->height-1-y, drawable->width);
}
}
else
{
/* copy and shift bits */
g_message("%d bits per sample not yet supported!", nBitsPS);
}
}
gimp_drawable_flush(drawable);
gimp_drawable_detach(drawable);
/* Don't miss to display the new image!
*/
if (bIsNewImage)
gimp_display_new (image_ID);
else
{
gimp_layer_set_visible(drawable_ID, TRUE);
gimp_displays_flush();
}
}
/* done */
/* clear clipboard? */
if (NULL != hDIB) GlobalFree(hDIB);
CloseClipboard();
/* shouldn't this be done by caller?? */
gimp_image_undo_enable(image_ID);
return bRet;
} /* CB_PasteImage */
/*
* Local Variables:
* tab-width: 4
* End:
*/