mirror of https://github.com/GNOME/gimp.git
652 lines
16 KiB
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:
|
|
*/
|