Commit 6d6f69b4 authored by Tor Lillqvist's avatar Tor Lillqvist
Browse files

New (Win32) print plug-in.

	* plug-ins/winprint: New (Win32) print plug-in.

	* plug-ins/makefile.{cygwin,msc}: Built winprint, not the Unixish
	print plug-in.
parent 49ac3a5e
1999-08-02 Tor Lillqvist <tml@iki.fi>
* plug-ins/winprint: New (Win32) print plug-in.
* plug-ins/makefile.{cygwin,msc}: Built winprint, not the Unixish
print plug-in.
Sun Aug 1 16:27:27 MEST 1999 Sven Neumann <sven@gimp.org>
* app/paint_core.c: Fixed a bug in the line-preview.
......
......@@ -29,7 +29,7 @@ COMMON4 = mapcolor max_rgb mblur newsprint nlfilt noisify normalize nova oilify
COMMON5 = sample_colorize scatter_hsv semiflatten sharpen shift smooth_palette snoise sobel sparkle spread sunras tga threshold_alpha tiff tile tileit tiler video vinvert vpropagate waves whirlpinch wind wmf xbm xwd zealouscrop
# These have own subdirectories each
SEPARATE = AlienMap FractalExplorer Lighting MapObject bmp borderaverage dbbrowser faxg3 fits flame fp gfig gfli ifscompose maze mosaic pagecurl print rcm sel2path sgi sinus struc unsharp winsnap
SEPARATE = AlienMap FractalExplorer Lighting MapObject bmp borderaverage dbbrowser faxg3 fits flame fp gfig gfli ifscompose maze mosaic pagecurl rcm sel2path sgi sinus struc unsharp winprint winsnap
# These are unofficial, ie not in the CVS. To build these, you should
# get tml's source snapshot and copy this makefile to the
......@@ -102,9 +102,11 @@ separate-plugins-clean :
sub-separate-plugins :
sub-separate-plugins : sub-script-fu
for d in $(SEPARATE); do $(MAKE) -f makefile.cygwin sub-one-separate DIR=$$d TARGET=$(TARGET); done
# We must handle script-fu separately because of the dash, sigh
sub-script-fu :
cd script-fu; $(MAKE) -f ../makefile.cygwin TOP=../.. PLUGIN=script-fu EXTRA_script_fu=1 $(TARGET)
sub-one-separate :
......
......@@ -28,7 +28,7 @@ COMMON4 = mapcolor max_rgb mblur newsprint nlfilt noisify normalize nova oilify
COMMON5 = sample_colorize scatter_hsv semiflatten sharpen shift smooth_palette snoise sobel sparkle spread sunras tga threshold_alpha tiff tile tileit tiler video vinvert vpropagate waves whirlpinch wind wmf xbm xwd zealouscrop
# These have own subdirectories each
SEPARATE = AlienMap FractalExplorer Lighting MapObject bmp borderaverage dbbrowser faxg3 fits flame fp gfig gfli ifscompose maze mosaic pagecurl print rcm sel2path sgi sinus struc unsharp winsnap
SEPARATE = AlienMap FractalExplorer Lighting MapObject bmp borderaverage dbbrowser faxg3 fits flame fp gfig gfli ifscompose maze mosaic pagecurl rcm sel2path sgi sinus struc unsharp winprint winsnap
# These are unofficial, ie not in the CVS. To build these, you should
# get tml's source snapshot and copy this makefile to the
......@@ -389,6 +389,10 @@ OBJECTS = \
EXTRALIBS = user32.lib
!ENDIF
!IFDEF EXTRA_winprint
EXTRALIBS = user32.lib gdi32.lib comdlg32.lib
!endif
!IFDEF EXTRA_winsnap
HAVE_RESOURCE = YES
EXTRALIBS = user32.lib gdi32.lib
......
/* Print plug-in for the GIMP on Windows.
* Copyright 1999 Tor Lillqvist <tml@iki.fi>
*
* 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.
*/
/*
* TODO:
*
* Own dialog box, with similar options as the Unix print plug-in?
* (at least those that aren't already covered in the Windows standard
* page setup and/or print dialogs):
* - brighness and gamma adjustment
* - scaling
* - image placement on paper (at least centering should be offered)
*
* Speed up the StretchBlt'ing. Now we StretchBlt one pixel row at
* a time, and in pieces. Quite ad hoc.
*
* Handle the file_print procedure's parameters as much as possible
* like the print plug-in does. (They are currently cheerfully ignored.)
*
* Etc.
*/
#include "config.h"
#include <stdlib.h>
#include <windows.h>
#include "libgimp/gimp.h"
#include "libgimp/stdplugins-intl.h"
#define NAME_PRINT "file_print"
#define NAME_PAGE_SETUP "file_page_setup"
#define PING() g_message ("%s: %d", __FILE__, __LINE__)
static struct
{
PRINTDLG prDlg;
PAGESETUPDLG psDlg;
int devmodeSize;
} vars =
{
{ 0, },
{ 0, },
0
};
/* These two functions lifted from the print plug-in and modified
* to build BGR format pixel rows.
*/
static void
indexed_to_bgr(guchar *indexed,
guchar *bgrout,
int width,
int bpp,
guchar *cmap,
int ncolours)
{
if (bpp == 1)
{
/* No alpha in image. */
while (width > 0)
{
bgrout[2] = cmap[*indexed * 3 + 0];
bgrout[1] = cmap[*indexed * 3 + 1];
bgrout[0] = cmap[*indexed * 3 + 2];
bgrout += 3;
indexed ++;
width --;
}
}
else
{
/* Indexed alpha image. */
while (width > 0)
{
bgrout[2] = cmap[indexed[0] * 3 + 0] * indexed[1] / 255 +
255 - indexed[1];
bgrout[1] = cmap[indexed[0] * 3 + 1] * indexed[1] / 255 +
255 - indexed[1];
bgrout[0] = cmap[indexed[0] * 3 + 2] * indexed[1] / 255 +
255 - indexed[1];
bgrout += 3;
indexed += bpp;
width --;
}
}
}
static void
rgb_to_bgr(guchar *rgbin,
guchar *bgrout,
int width,
int bpp,
guchar *cmap,
int ncolours)
{
if (bpp == 3)
{
/* No alpha in image. */
while (width > 0)
{
bgrout[2] = rgbin[0];
bgrout[1] = rgbin[1];
bgrout[0] = rgbin[2];
rgbin += 3;
bgrout += 3;
width --;
}
}
else
{
/* RGBA image. */
while (width > 0)
{
bgrout[2] = rgbin[0] * rgbin[3] / 255 + 255 - rgbin[3];
bgrout[1] = rgbin[1] * rgbin[3] / 255 + 255 - rgbin[3];
bgrout[0] = rgbin[2] * rgbin[3] / 255 + 255 - rgbin[3];
rgbin += bpp;
bgrout += 3;
width --;
}
}
}
/* Respond to a plug-in query. */
static void
query(void)
{
static GParamDef print_args[] =
{
{ PARAM_INT32, "run_mode", "Interactive, non-interactive" },
{ PARAM_IMAGE, "image", "Input image" },
{ PARAM_DRAWABLE, "drawable", "Input drawable" },
{ PARAM_STRING, "printer", "Printer" },
{ PARAM_STRING, "ppd_file", "PPD file" },
{ PARAM_INT32, "output_type", "Output type (0 = gray, 1 = color)" },
{ PARAM_STRING, "resolution", "Resolution (\"300\", \"720\", etc.)" },
{ PARAM_STRING, "media_size", "Media size (\"Letter\", \"A4\", etc.)" },
{ PARAM_STRING, "media_type", "Media type (\"Plain\", \"Glossy\", etc.)" },
{ PARAM_STRING, "media_source", "Media source (\"Tray1\", \"Manual\", etc.)" },
{ PARAM_INT32, "brightness", "Brightness (0-200%)" },
{ PARAM_FLOAT, "scaling", "Output scaling (0-100%, -PPI)" },
{ PARAM_INT32, "orientation", "Output orientation (-1 = auto, 0 = portrait, 1 = landscape)" },
{ PARAM_INT32, "left", "Left offset (points, -1 = centered)" },
{ PARAM_INT32, "top", "Top offset (points, -1 = centered)" }
};
static int print_nargs =
sizeof(print_args) / sizeof(print_args[0]);
static GParamDef pagesetup_args[] =
{
{ PARAM_INT32, "run_mode", "Interactive, non-interactive" },
{ PARAM_IMAGE, "image", "Input image" },
{ PARAM_DRAWABLE, "drawable", "Input drawable" },
};
static int pagesetup_nargs =
sizeof(pagesetup_args) / sizeof(pagesetup_args[0]);
INIT_I18N();
gimp_install_procedure(
NAME_PRINT,
_("This plug-in prints images from the GIMP."),
_("Prints images to any printer recognized by Windows."),
"Tor Lillqvist <tml@iki.fi>",
"Copyright 1999 Tor Lillqvist",
"$Id$",
_("<Image>/File/Print"),
"RGB*,GRAY*,INDEXED*",
PROC_PLUG_IN,
print_nargs,
0,
print_args,
NULL);
gimp_install_procedure(
NAME_PAGE_SETUP,
_("This plug-in sets up the page for printing from the GIMP."),
_("Sets up the page parameters for printing to any Windows printer."),
"Tor Lillqvist <tml@iki.fi>",
"Copyright 1999 Tor Lillqvist",
"$Id$",
_("<Image>/File/Page Setup"),
"RGB*,GRAY*,INDEXED*",
PROC_PLUG_IN,
pagesetup_nargs,
0,
pagesetup_args,
NULL);
}
/*
* 'run()' - Run the plug-in...
*/
static void
run (char *name,
int nparams,
GParam *param,
int *nreturn_vals,
GParam **return_vals)
{
GDrawable *drawable;
GRunModeType run_mode;
GStatusType status = STATUS_SUCCESS;
GParam *values;
GPixelRgn rgn;
guchar *cmap; /* Colourmap (indexed images only) */
DEVMODE *dmp;
int ncolours;
int width, height;
int devWidth, devHeight;
int y;
double devY, devYstep;
int iDevY, iDevYstep;
guchar *inRow;
guchar *bgrRow;
HGLOBAL hDevMode, hDevNames;
DOCINFO docInfo;
double devResX, devResY;
double imgResX, imgResY;
HDC hdcMem;
HBITMAP hBitmap;
HANDLE oldBm;
BITMAPV4HEADER bmHeader;
INIT_I18N();
run_mode = param[0].data.d_int32;
values = g_new(GParam, 1);
values[0].type = PARAM_STATUS;
*nreturn_vals = 1;
*return_vals = values;
hDevMode = NULL;
hDevNames = NULL;
drawable = gimp_drawable_get(param[2].data.d_drawable);
width = drawable->width;
height = drawable->height;
if (strcmp (name, NAME_PRINT) == 0)
{
switch (run_mode)
{
case RUN_INTERACTIVE:
vars.devmodeSize = 0;
vars.prDlg.lStructSize = 0;
gimp_get_data (NAME_PRINT, &vars);
if (vars.devmodeSize > 0)
{
/* Restore saved DEVMODE. */
hDevMode = GlobalAlloc (GMEM_MOVEABLE, vars.devmodeSize);
g_assert (hDevMode != NULL);
dmp = GlobalLock (hDevMode);
g_assert (dmp != NULL);
gimp_get_data (NAME_PRINT "devmode", dmp);
GlobalUnlock (hDevMode);
vars.prDlg.hDevMode = hDevMode;
}
else
{
vars.prDlg.hDevMode = NULL;
}
if (vars.prDlg.lStructSize == 0)
{
vars.prDlg.lStructSize = sizeof (PRINTDLG);
vars.prDlg.Flags = 0;
}
vars.prDlg.hwndOwner = NULL;
vars.prDlg.hDevNames = NULL;
vars.prDlg.Flags |=
PD_RETURNDC | PD_DISABLEPRINTTOFILE | PD_HIDEPRINTTOFILE
| PD_NOSELECTION;
vars.prDlg.nMinPage = vars.prDlg.nMaxPage = 0;
vars.prDlg.nCopies = 1;
if (!PrintDlg (&vars.prDlg))
{
if (CommDlgExtendedError ())
g_message (_("PrintDlg failed: %d"),
CommDlgExtendedError ());
status = STATUS_EXECUTION_ERROR;
break;
}
hDevMode = vars.prDlg.hDevMode;
hDevNames = vars.prDlg.hDevNames;
break;
case RUN_NONINTERACTIVE:
if (nparams >= 3) /* Printer name? */
{
}
if (nparams >= 5) /* PPD file */
{
/* Ignored */
}
status = STATUS_EXECUTION_ERROR;
break;
default:
status = STATUS_CALLING_ERROR;
break;
}
/*
* Print the image.
*/
/* Check if support for BitBlt */
if (status == STATUS_SUCCESS)
{
/* Check if support for BitBlt */
if (!(GetDeviceCaps(vars.prDlg.hDC, RASTERCAPS) & RC_BITBLT))
{
status = STATUS_EXECUTION_ERROR;
g_message (_("Printer doesn't support bitmaps"));
}
}
if (status == STATUS_SUCCESS)
{
/* Set the tile cache size. */
if (height > width)
gimp_tile_cache_ntiles((drawable->height + gimp_tile_width() - 1) /
gimp_tile_width() + 1);
else
gimp_tile_cache_ntiles((width + gimp_tile_width() - 1) /
gimp_tile_width() + 1);
/* Is the image indexed? If so we need the colourmap. */
if (gimp_image_base_type(param[1].data.d_image) == INDEXED)
cmap = gimp_image_get_cmap(param[1].data.d_image, &ncolours);
else
{
cmap = NULL;
ncolours = 0;
}
/* Start print job. */
docInfo.cbSize = sizeof (DOCINFO);
docInfo.lpszDocName =
gimp_image_get_filename (param[1].data.d_image);
docInfo.lpszOutput = NULL;
docInfo.lpszDatatype = NULL;
docInfo.fwType = 0;
if (StartDoc (vars.prDlg.hDC, &docInfo) == SP_ERROR)
status = STATUS_EXECUTION_ERROR;
}
if (status == STATUS_SUCCESS)
{
/* Prepare printer to accept a page. */
if (StartPage (vars.prDlg.hDC) <= 0)
{
status = STATUS_EXECUTION_ERROR;
g_message (_("StartPage failed"));
AbortDoc (vars.prDlg.hDC);
}
}
/* Actually print. */
if (status == STATUS_SUCCESS)
{
gimp_progress_init(_("Printing..."));
gimp_pixel_rgn_init(&rgn, drawable, 0, 0,
width, height,
FALSE, FALSE);
inRow = g_malloc (width * drawable->bpp);
hdcMem = CreateCompatibleDC (vars.prDlg.hDC);
bmHeader.bV4Size = sizeof (BITMAPV4HEADER);
bmHeader.bV4Width = width;
bmHeader.bV4Height = -1;
bmHeader.bV4Planes = 1;
bmHeader.bV4BitCount = 24;
bmHeader.bV4V4Compression = BI_RGB;
bmHeader.bV4SizeImage = 0;
bmHeader.bV4XPelsPerMeter = 0;
bmHeader.bV4YPelsPerMeter = 0;
bmHeader.bV4ClrUsed = 0;
bmHeader.bV4ClrImportant = 0;
bmHeader.bV4CSType = 0;
hBitmap = CreateDIBSection (hdcMem,
(BITMAPINFO *) &bmHeader,
DIB_RGB_COLORS,
(PVOID *) &bgrRow,
NULL,
0);
if (hBitmap == NULL)
{
status = STATUS_EXECUTION_ERROR;
g_message (_("CreateDIBSection failed"));
AbortDoc (vars.prDlg.hDC);
}
}
if (status == STATUS_SUCCESS)
{
void (*pixel_transfer)(guchar *, guchar *, int, int, guchar *, int);
if (cmap != NULL)
pixel_transfer = indexed_to_bgr;
else
pixel_transfer = rgb_to_bgr;
devResX = GetDeviceCaps(vars.prDlg.hDC, LOGPIXELSX);
devResY = GetDeviceCaps(vars.prDlg.hDC, LOGPIXELSY);
gimp_image_get_resolution (param[1].data.d_image,
&imgResX, &imgResY);
/* Here we assume that the printer's resolution is many
* times higher than the image's. Otherwise we probably
* get strange artefacts from StretchBlt'ing a row at
* a time, if each image row maps to a little over one
* printer row (or even less than one)?
*/
devWidth = (devResX / imgResX) * width;
devHeight = (devResY / imgResY) * height;
devYstep = (double) devHeight / height;
#if 0
g_message ("devWidth = %d, devHeight = %d, devYstep = %g",
devWidth, devHeight, devYstep);
#endif
if (!SetStretchBltMode (vars.prDlg.hDC, HALFTONE))
g_message (_("SetStretchBltMode failed (warning only)"));
oldBm = SelectObject (hdcMem, hBitmap);
devY = 0.0;
iDevY = 0;
for (y = 0; y < height; y++)
{
if ((y & 0x0F) == 0)
gimp_progress_update ((double)y / (double)drawable->height);
gimp_pixel_rgn_get_row (&rgn, inRow, 0, y, width);
(* pixel_transfer) (inRow, bgrRow, width, drawable->bpp, cmap, ncolours);
iDevYstep = ((int) (devY + devYstep)) - iDevY;
if (iDevYstep > 0)
{
/* StretchBlt seems to fail if we do it in "too large"
* pieces at a time, a least with my printer.
* Arbitrarily limit to 4000 destination columns
* at a time...
*/
int x, iDevX, xstep, iDevXstep;
iDevX = 0;
x = 0;
iDevXstep = 4000;
xstep = (imgResX / devResX ) * iDevXstep;
while (x <= width)
{
int w, devW;
if (x + xstep > width)
w = width - x;
else
w = xstep;
if (iDevX + iDevXstep > devWidth)
devW = devWidth - iDevX;
else
devW = iDevXstep;
if (!StretchBlt (vars.prDlg.hDC, iDevX, iDevY,
devW, iDevYstep,
hdcMem, x, 0, w, 1, SRCCOPY))
{
status = STATUS_EXECUTION_ERROR;
g_message (_("StretchBlt (hDC, %d, %d, "
"%d, %d, "
"hdcMem, %d, 0, %d, 1, SRCCOPY) "
"failed, error = %d, y = %d"),
iDevX, iDevY,
devW, iDevYstep,
x, w,
GetLastError (), y);
AbortDoc (vars.prDlg.hDC);
break;
}
x += xstep;
iDevX += iDevXstep;
}
}
devY += devYstep;
iDevY += iDevYstep;
}
SelectObject (hdcMem, oldBm);
DeleteObject (hBitmap);
gimp_progress_update (1.0);
}
if (status == STATUS_SUCCESS)
{
if (EndPage (vars.prDlg.hDC) <= 0)
{
status = STATUS_EXECUTION_ERROR;
g_message (_("EndPage failed"));
EndDoc (vars.prDlg.hDC);
}
}
if (status == STATUS_SUCCESS)
{
EndDoc (vars.prDlg.hDC);
}
DeleteDC (vars.prDlg.hDC);
}
else if (strcmp (name, NAME_PAGE_SETUP) == 0)
{
switch (run_mode)
{
case RUN_INTERACTIVE:
vars.devmodeSize = 0;
vars.psDlg.lStructSize = 0;
gimp_get_data (NAME_PRINT, &vars);
if (vars.devmodeSize > 0)
{
/* Restore saved DEVMODE. */
hDevMode = GlobalAlloc (GMEM_MOVEABLE, vars.devmodeSize);
g_assert (hDevMode != NULL);
dmp = GlobalLock (hDevMode);
g_assert (dmp != NULL);
gimp_get_data (NAME_PRINT "devmode", dmp);
GlobalUnlock (hDevMode);
vars.psDlg.hDevMode = hDevMode;
}
else
{
vars.psDlg.hDevMode = NULL;
}