1 /* Copyright (C) 2001-2008 Artifex Software, Inc.
2    All Rights Reserved.
3 
4    This software is provided AS-IS with no warranty, either express or
5    implied.
6 
7    This software is distributed under license and may not be copied, modified
8    or distributed except as expressly authorized under the terms of that
9    license.  Refer to licensing information at http://www.artifex.com/
10    or contact Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134,
11    San Rafael, CA  94903, U.S.A., +1(415)492-9861, for further information.
12 */
13 
14 /* $Id: dwimg.c 9731 2009-05-08 13:13:26Z ken $ */
15 
16 /* display device image window for Windows */
17 
18 /* This code supports both single threaded and multithreaded operation */
19 /* For multithread, access is shared as follows:
20  * Each image has a Mutex img->hmutex, used for protected access to
21  * the img->image and its dimensions.
22  * Main thread can access
23  *   image_find()
24  *   image_new()
25  *   image_delete()
26  *   image_size()
27  * Main thread must acquire mutex on display_presize() and release
28  * in display_size() after image_size() is called.
29  * Main thread must acquire mutex on display_preclose().
30  *
31  * Second thread must not access image_find, image_new, image_delete
32  * or image_size.  It must grab mutex before accessing img->image.
33  */
34 
35 #define STRICT
36 #include <windows.h>
37 #include "stdio_.h"
38 
39 #include "dwres.h"
40 #include "dwimg.h"
41 #include "dwreg.h"
42 #include "gdevdsp.h"
43 
44 
45 static const char szImgName2[] = "Ghostscript Image";
46 static const char szTrcName2[] = "Ghostscript Graphical Trace";
47 
48 /* Forward references */
49 LRESULT CALLBACK WndImg2Proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
50 
51 static void register_class(void);
52 static void draw(IMAGE *img, HDC hdc, int dx, int dy, int wx, int wy,
53     int sx, int sy);
54 static HGLOBAL copy_dib(IMAGE *img);
55 static HPALETTE create_palette(IMAGE *img);
56 static void create_window(IMAGE *img);
57 
58 #define M_COPY_CLIP 1
59 #define M_DEVICEN_GRAY 2	/* show single separation as gray */
60 #define M_SEPARATION 3 		/* 3 to 3+IMG_DEVICEN_MAX-1 */
61 
62 #define DISPLAY_ERROR (-1)	/* return this to Ghostscript on error */
63 
64 /* Define  min and max, but make sure to use the identical definition */
65 /* to the one that all the compilers seem to have.... */
66 #ifndef min
67 #  define min(a, b) (((a) < (b)) ? (a) : (b))
68 #endif
69 #ifndef max
70 #  define max(a, b) (((a) > (b)) ? (a) : (b))
71 #endif
72 
73 
74 /* GUI thread only */
75 void image_color(unsigned int format, int index,
76     unsigned char *r, unsigned char *g, unsigned char *b);
77 void image_convert_line(IMAGE *img, unsigned char *dest, unsigned char *source);
78 void image_16BGR555_to_24BGR(int width, unsigned char *dest,
79     unsigned char *source);
80 void image_16BGR565_to_24BGR(int width, unsigned char *dest,
81     unsigned char *source);
82 void image_16RGB555_to_24BGR(int width, unsigned char *dest,
83     unsigned char *source);
84 void image_16RGB565_to_24BGR(int width, unsigned char *dest,
85     unsigned char *source);
86 void image_4CMYK_to_24BGR(int width, unsigned char *dest,
87     unsigned char *source, IMAGE_DEVICEN *devicen, int devicen_gray);
88 void image_32CMYK_to_24BGR(int width, unsigned char *dest,
89     unsigned char *source, IMAGE_DEVICEN *devicen, int devicen_gray);
90 void image_devicen_to_24BGR(int width, unsigned char *dest,
91     unsigned char *source, IMAGE_DEVICEN *devicen, int devicen_gray);
92 
93 
94 /****************************************************************/
95 /* These functions are only accessed by the main thread */
96 
97 IMAGE *first_image = NULL;
98 static HWND img_hwndtext = (HWND)0;
99 
100 void
image_textwindow(HWND hwnd)101 image_textwindow(HWND hwnd)
102 {
103     /* Save the handle to the text window in a global variable
104      * (if running as a GUI) so we can redirect keyboard input
105      * to the text window.
106      * If set to 0, then assume we are using console mode.
107      */
108     img_hwndtext = hwnd;
109 }
110 
111 /* image_find must only be accessed by main thread */
112 /* valid for main thread */
113 IMAGE *
image_find(void * handle,void * device)114 image_find(void *handle, void *device)
115 {
116     IMAGE *img;
117     for (img = first_image; img!=0; img=img->next) {
118 	if ((img->handle == handle) && (img->device == device))
119 	    return img;
120     }
121     return NULL;
122 }
123 
124 /* image_find must only be accessed by main thread */
125 /* valid for main thread */
126 IMAGE *
image_new(void * handle,void * device)127 image_new(void *handle, void *device)
128 {
129     IMAGE *img = (IMAGE *)malloc(sizeof(IMAGE));
130     if (img) {
131         memset(img, 0, sizeof(IMAGE));
132 	/* remember device and handle */
133 	img->handle = handle;
134 	img->device = device;
135 	img->hwndtext = img_hwndtext;
136 
137 	img->update_tick = 100;		/* milliseconds */
138 	img->update_interval = 1;	/* 1 tick */
139 	img->update_count = 0;
140 
141         img->hmutex = INVALID_HANDLE_VALUE;
142 
143 	/* add to list */
144 	img->next = first_image;
145 	first_image = img;
146     }
147     return img;
148 }
149 
150 /* remove image from linked list */
151 /* valid for main thread */
152 void
image_delete(IMAGE * img)153 image_delete(IMAGE *img)
154 {
155     /* remove from list */
156     if (img == first_image) {
157 	first_image = img->next;
158     }
159     else {
160 	IMAGE *tmp;
161 	for (tmp = first_image; tmp!=0; tmp=tmp->next) {
162 	    if (img == tmp->next)
163 		tmp->next = img->next;
164 	}
165     }
166     /* Note: img is freed by image_close, not image_delete */
167 }
168 
169 
170 /* resize image */
171 /* valid for main thread */
172 int
image_size(IMAGE * img,int new_width,int new_height,int new_raster,unsigned int new_format,void * pimage)173 image_size(IMAGE *img, int new_width, int new_height, int new_raster,
174     unsigned int new_format, void *pimage)
175 {
176     int i;
177     img->raster = new_raster;
178     img->format = new_format;
179     img->image = (unsigned char *)pimage;
180 
181     /* create a BMP header for the bitmap */
182     img->bmih.biSize = sizeof(BITMAPINFOHEADER);
183     img->bmih.biWidth = new_width;
184     img->bmih.biHeight = new_height;
185     img->bmih.biPlanes = 1;
186 
187     /* Reset separations */
188     for (i=0; i<IMAGE_DEVICEN_MAX; i++) {
189 	img->devicen[i].used = 0;
190 	img->devicen[i].visible = 1;
191 	memset(img->devicen[i].name, 0, sizeof(img->devicen[i].name));
192 	img->devicen[i].cyan = 0;
193 	img->devicen[i].magenta = 0;
194 	img->devicen[i].yellow = 0;
195 	img->devicen[i].black = 0;
196     }
197 
198     switch (img->format & DISPLAY_COLORS_MASK) {
199 	case DISPLAY_COLORS_NATIVE:
200 	    switch (img->format & DISPLAY_DEPTH_MASK) {
201 		case DISPLAY_DEPTH_1:
202 		    img->bmih.biBitCount = 1;
203 		    img->bmih.biClrUsed = 2;
204 		    img->bmih.biClrImportant = 2;
205 		    break;
206 		case DISPLAY_DEPTH_4:
207 		    /* Fixed color palette */
208 		    img->bmih.biBitCount = 4;
209 		    img->bmih.biClrUsed = 16;
210 		    img->bmih.biClrImportant = 16;
211 		    break;
212 		case DISPLAY_DEPTH_8:
213 		    /* Fixed color palette */
214 		    img->bmih.biBitCount = 8;
215 		    img->bmih.biClrUsed = 96;
216 		    img->bmih.biClrImportant = 96;
217 		    break;
218 		case DISPLAY_DEPTH_16:
219 		    /* RGB bitfields */
220 		    /* Bit fields */
221 		    if ((img->format & DISPLAY_ENDIAN_MASK)
222 			== DISPLAY_BIGENDIAN) {
223 			/* convert to 24BGR */
224 			img->bmih.biBitCount = 24;
225 			img->bmih.biClrUsed = 0;
226 			img->bmih.biClrImportant = 0;
227 		    }
228 		    else {
229 			img->bmih.biBitCount = 16;
230 			img->bmih.biClrUsed = 0;
231 			img->bmih.biClrImportant = 0;
232 		    }
233 		    break;
234 		default:
235 		    return DISPLAY_ERROR;
236 	    }
237 	    break;
238 	case DISPLAY_COLORS_GRAY:
239 	    switch (img->format & DISPLAY_DEPTH_MASK) {
240 		case DISPLAY_DEPTH_1:
241 		    img->bmih.biBitCount = 1;
242 		    img->bmih.biClrUsed = 2;
243 		    img->bmih.biClrImportant = 2;
244 		    break;
245 		case DISPLAY_DEPTH_4:
246 		    /* Fixed gray palette */
247 		    img->bmih.biBitCount = 4;
248 		    img->bmih.biClrUsed = 16;
249 		    img->bmih.biClrImportant = 16;
250 		    break;
251 		case DISPLAY_DEPTH_8:
252 		    /* Fixed gray palette */
253 		    img->bmih.biBitCount = 8;
254 		    img->bmih.biClrUsed = 256;
255 		    img->bmih.biClrImportant = 256;
256 		    break;
257 		default:
258 		    return DISPLAY_ERROR;
259 	    }
260 	    break;
261 	case DISPLAY_COLORS_RGB:
262 	    if ((img->format & DISPLAY_DEPTH_MASK) != DISPLAY_DEPTH_8)
263 		return DISPLAY_ERROR;
264 	    if (((img->format & DISPLAY_ALPHA_MASK) == DISPLAY_UNUSED_LAST) &&
265 		((img->format & DISPLAY_ENDIAN_MASK) == DISPLAY_LITTLEENDIAN)) {
266 		/* use bitfields to display this */
267 		img->bmih.biBitCount = 32;
268 		img->bmih.biClrUsed = 0;
269 		img->bmih.biClrImportant = 0;
270 	    }
271 	    else {
272 		/* either native BGR, or we need to convert it */
273 		img->bmih.biBitCount = 24;
274 		img->bmih.biClrUsed = 0;
275 		img->bmih.biClrImportant = 0;
276 	    }
277 	    break;
278 	case DISPLAY_COLORS_CMYK:
279 	    switch (img->format & DISPLAY_DEPTH_MASK) {
280 		case DISPLAY_DEPTH_1:
281 		case DISPLAY_DEPTH_8:
282 		    /* we can convert these formats */
283 		    break;
284 		default:
285 		    return DISPLAY_ERROR;
286 	    }
287 	    /* we can't display this natively */
288 	    /* we will convert it just before displaying */
289 	    img->bmih.biBitCount = 24;
290 	    img->bmih.biClrUsed = 0;
291 	    img->bmih.biClrImportant = 0;
292 	    img->devicen[0].used = 1;
293 	    img->devicen[0].cyan = 65535;
294 	    /* We already know about the CMYK components */
295 	    strncpy(img->devicen[0].name, "Cyan",
296 		sizeof(img->devicen[0].name));
297 	    img->devicen[1].used = 1;
298 	    img->devicen[1].magenta = 65535;
299 	    strncpy(img->devicen[1].name, "Magenta",
300 		sizeof(img->devicen[1].name));
301 	    img->devicen[2].used = 1;
302 	    img->devicen[2].yellow = 65535;
303 	    strncpy(img->devicen[2].name, "Yellow",
304 		sizeof(img->devicen[2].name));
305 	    img->devicen[3].used = 1;
306 	    img->devicen[3].black = 65535;
307 	    strncpy(img->devicen[3].name, "Black",
308 		sizeof(img->devicen[3].name));
309 	    break;
310 	case DISPLAY_COLORS_SEPARATION:
311 	    /* we can't display this natively */
312 	    /* we will convert it just before displaying */
313 	    img->bmih.biBitCount = 24;
314 	    img->bmih.biClrUsed = 0;
315 	    img->bmih.biClrImportant = 0;
316 	    break;
317     }
318 
319     img->bmih.biCompression = 0;
320     img->bmih.biSizeImage = 0;
321     img->bmih.biXPelsPerMeter = 0;
322     img->bmih.biYPelsPerMeter = 0;
323     img->bytewidth = ((img->bmih.biWidth * img->bmih.biBitCount + 31 ) & ~31) >> 3;
324 
325     if (img->palette)
326 	DeleteObject(img->palette);
327     img->palette = create_palette(img);
328 
329     return 0;
330 }
331 
332 int
image_separation(IMAGE * img,int comp_num,const char * name,unsigned short c,unsigned short m,unsigned short y,unsigned short k)333 image_separation(IMAGE *img,
334     int comp_num, const char *name,
335     unsigned short c, unsigned short m,
336     unsigned short y, unsigned short k)
337 {
338     if ((comp_num < 0) || (comp_num > IMAGE_DEVICEN_MAX))
339 	return DISPLAY_ERROR;
340     img->devicen[comp_num].used = 1;
341     strncpy(img->devicen[comp_num].name, name,
342 	sizeof(img->devicen[comp_num].name)-1);
343     img->devicen[comp_num].cyan    = c;
344     img->devicen[comp_num].magenta = m;
345     img->devicen[comp_num].yellow  = y;
346     img->devicen[comp_num].black   = k;
347     return 0;
348 }
349 
350 
351 /****************************************************************/
352 /* These functions are only accessed by the GUI thread */
353 
354 /* open window for device and add to list */
355 void
image_open(IMAGE * img)356 image_open(IMAGE *img)
357 {
358     /* register class */
359     register_class();
360 
361     /* open window */
362     create_window(img);
363 }
364 
365 /* close window and remove from list */
366 void
image_close(IMAGE * img)367 image_close(IMAGE *img)
368 {
369     DestroyWindow(img->hwnd);
370     img->hwnd = NULL;
371 
372     if (img->palette)
373 	DeleteObject(img->palette);
374     img->palette = NULL;
375 
376     if (img->hBrush)
377 	DeleteObject(img->hBrush);
378     img->hBrush = NULL;
379 
380     free(img);
381 }
382 
383 
384 void
register_class(void)385 register_class(void)
386 {
387     WNDCLASS wndclass;
388     HINSTANCE hInstance = GetModuleHandle(NULL);
389 
390     /* register the window class for graphics */
391     wndclass.style = CS_HREDRAW | CS_VREDRAW;
392     wndclass.lpfnWndProc = WndImg2Proc;
393     wndclass.cbClsExtra = 0;
394     wndclass.cbWndExtra = sizeof(LONG);
395     wndclass.hInstance = hInstance;
396     wndclass.hIcon = LoadIcon(hInstance,(LPSTR)MAKEINTRESOURCE(GSIMAGE_ICON));
397     wndclass.hCursor = LoadCursor((HINSTANCE)NULL, IDC_ARROW);
398     wndclass.hbrBackground = NULL;	/* we will paint background */
399     wndclass.lpszMenuName = NULL;
400     wndclass.lpszClassName = szImgName2;
401     RegisterClass(&wndclass);
402 }
403 
image_separations(IMAGE * img)404 void image_separations(IMAGE *img)
405 {
406     char buf[64];
407     int i;
408     int exist;
409     int num_visible = 0;
410     HMENU sysmenu = GetSystemMenu(img->hwnd, FALSE);
411     if (((img->format & DISPLAY_COLORS_MASK) == DISPLAY_COLORS_CMYK) ||
412         ((img->format & DISPLAY_COLORS_MASK) == DISPLAY_COLORS_SEPARATION)) {
413 	/* Add menus if needed */
414 	for (i=0; i<IMAGE_DEVICEN_MAX; i++) {
415 	    exist = 0;
416 	    if (img->devicen[i].menu)
417 		exist = GetMenuString(sysmenu, M_SEPARATION+i,
418 			buf, sizeof(buf)-1, MF_BYCOMMAND) != 0;
419 	    if (exist && (strcmp(img->devicen[i].name, buf) != 0)) {
420 		/* remove it because name changed */
421 	       RemoveMenu(sysmenu, M_SEPARATION+i, MF_BYCOMMAND);
422 	       img->devicen[i].menu = 0;
423 	    }
424 	    if (img->devicen[i].name[0] && !img->devicen[i].menu) {
425 		AppendMenu(sysmenu, MF_STRING | MF_CHECKED,
426 		    M_SEPARATION+i, img->devicen[i].name);
427 		img->devicen[i].menu = 1;
428 	    }
429 	    if (img->devicen[i].used && img->devicen[i].visible)
430 		num_visible++;
431 	}
432 	EnableMenuItem(sysmenu, M_DEVICEN_GRAY,
433 	    MF_BYCOMMAND | ((num_visible <= 1) ? MF_ENABLED : MF_GRAYED));
434     }
435     else {
436 	for (i=0; i<IMAGE_DEVICEN_MAX; i++) {
437 	   if (img->devicen[i].menu) {
438 	       RemoveMenu(sysmenu, M_SEPARATION+i, MF_BYCOMMAND);
439 	       img->devicen[i].menu = 0;
440 	   }
441 	}
442 	EnableMenuItem(sysmenu, M_DEVICEN_GRAY, MF_BYCOMMAND | MF_GRAYED);
443     }
444 }
445 
sep_menu(IMAGE * img,int component)446 void sep_menu(IMAGE *img, int component)
447 {
448     int i;
449     int num_visible = 0;
450     img->devicen[component].visible = !img->devicen[component].visible;
451     CheckMenuItem(GetSystemMenu(img->hwnd, FALSE),
452 	M_SEPARATION+component,
453 	(img->devicen[component].visible ? MF_CHECKED : MF_UNCHECKED));
454     for (i=0; i<IMAGE_DEVICEN_MAX; i++)
455         if (img->devicen[i].used && img->devicen[i].visible)
456 	    num_visible++;
457     EnableMenuItem(GetSystemMenu(img->hwnd, FALSE), M_DEVICEN_GRAY,
458 	MF_BYCOMMAND | ((num_visible <= 1) ? MF_ENABLED : MF_GRAYED));
459     InvalidateRect(img->hwnd, NULL, 0);
460     UpdateWindow(img->hwnd);
461 }
462 
463 
464 static void
create_window(IMAGE * img)465 create_window(IMAGE *img)
466 {
467     HMENU sysmenu;
468     LOGBRUSH lb;
469     char winposbuf[256];
470     char window_title[256];
471     int len = sizeof(winposbuf);
472 
473     /* create background brush */
474     lb.lbStyle = BS_SOLID;
475     lb.lbHatch = 0;
476     lb.lbColor = GetSysColor(COLOR_WINDOW);
477     if (lb.lbColor = RGB(255,255,255)) /* Don't allow white */
478 	lb.lbColor = GetSysColor(COLOR_MENU);
479     if (lb.lbColor = RGB(255,255,255)) /* Don't allow white */
480 	lb.lbColor = GetSysColor(COLOR_APPWORKSPACE);
481     if (lb.lbColor = RGB(255,255,255)) /* Don't allow white */
482 	lb.lbColor = RGB(192,192,192);
483     img->hBrush = CreateBrushIndirect(&lb);
484 
485     img->cxClient = img->cyClient = 0;
486     img->nVscrollPos = img->nVscrollMax = 0;
487     img->nHscrollPos = img->nHscrollMax = 0;
488     img->x = img->y = img->cx = img->cy = CW_USEDEFAULT;
489     if (win_get_reg_value((img->device != NULL ? "Image" : "Tracer"), winposbuf, &len) == 0) {
490 	int x, y, cx, cy;
491 
492 	if (sscanf(winposbuf, "%d %d %d %d", &x, &y, &cx, &cy) == 4) {
493 	    img->x = x;
494 	    img->y = y;
495 	    img->cx = cx;
496 	    img->cy = cy;
497 	}
498     }
499     strcpy(window_title, (img->device != NULL ? (LPSTR)szImgName2 : (LPSTR)szTrcName2));
500     {  /*
501         *   This section is for debug purpose only.
502 	*   It allows to replace window title so that user can identify window
503 	*   when multiple instances of the application run in same time.
504 	*   Create gs\bin\gswin32.ini or gs\bin\gswin32c.ini and
505 	*   put an identifier to there like this :
506 	*
507 	*	[Window]
508 	*	Title=Current Revision
509 	*
510 	*   It is useful to compare images generated with different revisions.
511 	*/
512         char ini_path[MAX_PATH];
513 	DWORD ini_path_length;
514 
515 	ini_path_length = GetModuleFileName(NULL, ini_path, sizeof(ini_path));
516 	if (ini_path_length > 0) {
517 	    int i = ini_path_length - 1;
518 	    for (; i>=0; i--)
519 		if(ini_path[i] == '.')
520 		    break;
521 	    if (i < sizeof(ini_path) - 4) {
522 		strcpy(ini_path + i, ".ini");
523 		GetPrivateProfileString("Window", "Title",
524 			(img->device != NULL ? (LPSTR)szImgName2 : (LPSTR)szTrcName2),
525  			window_title, sizeof(window_title), ini_path);
526 	    }
527 	}
528     }
529     /* create window */
530     img->hwnd = CreateWindow(szImgName2, window_title,
531 	      WS_OVERLAPPEDWINDOW,
532 	      img->x, img->y, img->cx, img->cy,
533 	      NULL, NULL, GetModuleHandle(NULL), (void *)img);
534     if (img->device == NULL && img->x != CW_USEDEFAULT &&
535 			       img->y != CW_USEDEFAULT &&
536 			       img->cx != CW_USEDEFAULT &&
537 			       img->cy != CW_USEDEFAULT)
538         MoveWindow(img->hwnd, img->x, img->y, img->cx, img->cy, FALSE);
539     ShowWindow(img->hwnd, (img->device != NULL ? SW_SHOWMINNOACTIVE : SW_SHOW));
540 
541     /* modify the menu to have the new items we want */
542     sysmenu = GetSystemMenu(img->hwnd, 0);	/* get the sysmenu */
543     AppendMenu(sysmenu, MF_SEPARATOR, 0, NULL);
544     AppendMenu(sysmenu, MF_STRING, M_COPY_CLIP, "Copy to Clip&board");
545     AppendMenu(sysmenu, MF_STRING, M_DEVICEN_GRAY, "Show as Gray");
546     AppendMenu(sysmenu, MF_SEPARATOR, 0, NULL);
547 
548     image_separations(img);
549 }
550 
551 
552 void
image_poll(IMAGE * img)553 image_poll(IMAGE *img)
554 {
555     if ((img->bmih.biWidth == 0) || (img->bmih.biHeight == 0))
556 	return;
557     img->pending_update = 1;
558     if (img->update_timer == 0) {
559 	img->update_timer = 1;
560 	img->update_count = 0;
561 	SetTimer(img->hwnd, img->update_timer, img->update_tick, NULL);
562     }
563 }
564 
565 /* Redraw the window, making sure that periodic updates don't take too long. */
566 void
image_update_now(IMAGE * img)567 image_update_now(IMAGE *img)
568 {
569     SYSTEMTIME t1;
570     SYSTEMTIME t2;
571     int delta;
572     if ( !IsWindow(img->hwnd) )	/* some clod closed the window */
573 	create_window(img);
574 
575     if ( !IsIconic(img->hwnd) ) {  /* redraw window */
576 	GetSystemTime(&t1);
577 	InvalidateRect(img->hwnd, NULL, 1);
578 	UpdateWindow(img->hwnd);
579 	GetSystemTime(&t2);
580 	/* Make sure the update interval is at least 10 times
581 	 * what it takes to paint the window
582 	 */
583 	delta = (t2.wSecond - t1.wSecond)*1000 +
584 		(t2.wMilliseconds - t1.wMilliseconds);
585 	if (delta < 0)
586 	    delta += 60000;
587 	delta = 10 * delta / img->update_tick + 1;
588 	if (delta > img->update_interval)
589 	    img->update_interval = delta;
590 	else if ((delta >= 2) &&
591 		 (delta < img->update_interval / 4))
592 	    img->update_interval = delta/2;
593     }
594     img->update_count = 0;
595 }
596 
597 
598 void
image_sync(IMAGE * img)599 image_sync(IMAGE *img)
600 {
601     if (img->update_timer) {
602 	/* stop timer when nothing is happening */
603 	KillTimer(img->hwnd, img->update_timer);
604         img->update_timer = 0;
605     }
606     img->pending_sync = 0;
607     image_update_now(img);
608     image_separations(img);
609     img->pending_update = 0;
610 }
611 
612 
613 void
image_page(IMAGE * img)614 image_page(IMAGE *img)
615 {
616     if (IsIconic(img->hwnd))    /* useless as an Icon so fix it */
617 	ShowWindow(img->hwnd, SW_SHOWNORMAL);
618     BringWindowToTop(img->hwnd);
619 
620     image_sync(img);
621 }
622 
623 
624 /* GUI thread */
625 void
image_updatesize(IMAGE * img)626 image_updatesize(IMAGE *img)
627 {
628     RECT rect;
629     int nSizeType;
630     image_separations(img);
631     /* update scroll bars */
632     if (!IsIconic(img->hwnd)) {
633 	if (IsZoomed(img->hwnd))
634 	    nSizeType = SIZE_MAXIMIZED;
635 	else
636 	    nSizeType = SIZE_RESTORED;
637 	GetClientRect(img->hwnd, &rect);
638 	SendMessage(img->hwnd, WM_SIZE, nSizeType,
639 	    MAKELONG(rect.right, rect.bottom));
640     }
641 }
642 
643 void
image_color(unsigned int format,int index,unsigned char * r,unsigned char * g,unsigned char * b)644 image_color(unsigned int format, int index,
645     unsigned char *r, unsigned char *g, unsigned char *b)
646 {
647     switch (format & DISPLAY_COLORS_MASK) {
648 	case DISPLAY_COLORS_NATIVE:
649 	    switch (format & DISPLAY_DEPTH_MASK) {
650 		case DISPLAY_DEPTH_1:
651 		    *r = *g = *b = (index ? 0 : 255);
652 		    break;
653 		case DISPLAY_DEPTH_4:
654 		    if (index == 7)
655 			*r = *g = *b = 170;
656 		    else if (index == 8)
657 			*r = *g = *b = 85;
658 		    else {
659 			int one = index & 8 ? 255 : 128;
660 			*r = (index & 4 ? one : 0);
661 			*g = (index & 2 ? one : 0);
662 			*b = (index & 1 ? one : 0);
663 		    }
664 		    break;
665 		case DISPLAY_DEPTH_8:
666 		    /* palette of 96 colors */
667 		    /* 0->63 = 00RRGGBB, 64->95 = 010YYYYY */
668 		    if (index < 64) {
669 			int one = 255 / 3;
670 			*r = ((index & 0x30) >> 4) * one;
671 			*g = ((index & 0x0c) >> 2) * one;
672 			*b =  (index & 0x03) * one;
673 		    }
674 		    else {
675 			int val = index & 0x1f;
676 			*r = *g = *b = (val << 3) + (val >> 2);
677 		    }
678 		    break;
679 	    }
680 	    break;
681 	case DISPLAY_COLORS_GRAY:
682 	    switch (format & DISPLAY_DEPTH_MASK) {
683 		case DISPLAY_DEPTH_1:
684 		    *r = *g = *b = (index ? 255 : 0);
685 		    break;
686 		case DISPLAY_DEPTH_4:
687 		    *r = *g = *b = (unsigned char)((index<<4) + index);
688 		    break;
689 		case DISPLAY_DEPTH_8:
690 		    *r = *g = *b = (unsigned char)index;
691 		    break;
692 	    }
693 	    break;
694     }
695 }
696 
697 
698 /* convert one line of 16BGR555 to 24BGR */
699 /* byte0=GGGBBBBB byte1=0RRRRRGG */
700 void
image_16BGR555_to_24BGR(int width,unsigned char * dest,unsigned char * source)701 image_16BGR555_to_24BGR(int width, unsigned char *dest, unsigned char *source)
702 {
703     int i;
704     WORD w;
705     unsigned char value;
706     for (i=0; i<width; i++) {
707 	w = source[0] + (source[1] << 8);
708 	value = w & 0x1f;		/* blue */
709 	*dest++ = (value << 3) + (value >> 2);
710 	value = (w >> 5) & 0x1f;	/* green */
711 	*dest++ = (value << 3) + (value >> 2);
712 	value = (w >> 10) & 0x1f;	/* red */
713 	*dest++ = (value << 3) + (value >> 2);
714 	source += 2;
715     }
716 }
717 
718 /* convert one line of 16BGR565 to 24BGR */
719 /* byte0=GGGBBBBB byte1=RRRRRGGG */
720 void
image_16BGR565_to_24BGR(int width,unsigned char * dest,unsigned char * source)721 image_16BGR565_to_24BGR(int width, unsigned char *dest, unsigned char *source)
722 {
723     int i;
724     WORD w;
725     unsigned char value;
726     for (i=0; i<width; i++) {
727 	w = source[0] + (source[1] << 8);
728 	value = w & 0x1f;		/* blue */
729 	*dest++ = (value << 3) + (value >> 2);
730 	value = (w >> 5) & 0x3f;	/* green */
731 	*dest++ = (value << 2) + (value >> 4);
732 	value = (w >> 11) & 0x1f;	/* red */
733 	*dest++ = (value << 3) + (value >> 2);
734 	source += 2;
735     }
736 }
737 
738 /* convert one line of 16RGB555 to 24BGR */
739 /* byte0=0RRRRRGG byte1=GGGBBBBB */
740 void
image_16RGB555_to_24BGR(int width,unsigned char * dest,unsigned char * source)741 image_16RGB555_to_24BGR(int width, unsigned char *dest, unsigned char *source)
742 {
743     int i;
744     WORD w;
745     unsigned char value;
746     for (i=0; i<width; i++) {
747 	w = (source[0] << 8) + source[1];
748 	value = w & 0x1f;		/* blue */
749 	*dest++ = (value << 3) + (value >> 2);
750 	value = (w >> 5) & 0x1f;	/* green */
751 	*dest++ = (value << 3) + (value >> 2);
752 	value = (w >> 10) & 0x1f;	/* red */
753 	*dest++ = (value << 3) + (value >> 2);
754 	source += 2;
755     }
756 }
757 
758 /* convert one line of 16RGB565 to 24BGR */
759 /* byte0=RRRRRGGG byte1=GGGBBBBB */
760 void
image_16RGB565_to_24BGR(int width,unsigned char * dest,unsigned char * source)761 image_16RGB565_to_24BGR(int width, unsigned char *dest, unsigned char *source)
762 {
763     int i;
764     WORD w;
765     unsigned char value;
766     for (i=0; i<width; i++) {
767 	w = (source[0] << 8) + source[1];
768 	value = w & 0x1f;		/* blue */
769 	*dest++ = (value << 3) + (value >> 2);
770 	value = (w >> 5) & 0x3f;	/* green */
771 	*dest++ = (value << 2) + (value >> 4);
772 	value = (w >> 11) & 0x1f;	/* red */
773 	*dest++ = (value << 3) + (value >> 2);
774 	source += 2;
775     }
776 }
777 
778 void
image_4CMYK_to_24BGR(int width,unsigned char * dest,unsigned char * source,IMAGE_DEVICEN * devicen,int devicen_gray)779 image_4CMYK_to_24BGR(int width, unsigned char *dest, unsigned char *source,
780     IMAGE_DEVICEN *devicen, int devicen_gray)
781 {
782     int i;
783     int cyan, magenta, yellow, black;
784     int vc = devicen[0].visible;
785     int vm = devicen[1].visible;
786     int vy = devicen[2].visible;
787     int vk = devicen[3].visible;
788     int vall = vc && vm && vy && vk;
789     int show_gray = (vc + vm + vy + vk == 1) && devicen_gray;
790     int value;
791     for (i=0; i<width; i++) {
792         value = source[i/2];
793 	if (i & 0)
794 	    value >>= 4;
795 	cyan = ((value >> 3) & 1) * 255;
796 	magenta = ((value >> 2) & 1) * 255;
797 	yellow = ((value >> 1) & 1) * 255;
798 	black = (value & 1) * 255;
799 	if (!vall) {
800 	    if (!vc)
801 		cyan = 0;
802 	    if (!vm)
803 		magenta = 0;
804 	    if (!vy)
805 		yellow = 0;
806 	    if (!vk)
807 		black = 0;
808 	    if (show_gray) {
809 		black += cyan + magenta + yellow;
810 		cyan = magenta = yellow = 0;
811 	    }
812 	}
813 	*dest++ = (255 - yellow)  * (255 - black)/255; /* blue */
814 	*dest++ = (255 - magenta) * (255 - black)/255; /* green */
815 	*dest++ = (255 - cyan)    * (255 - black)/255; /* red */
816     }
817 }
818 
819 /* convert one line of 32CMYK to 24BGR */
820 void
image_32CMYK_to_24BGR(int width,unsigned char * dest,unsigned char * source,IMAGE_DEVICEN * devicen,int devicen_gray)821 image_32CMYK_to_24BGR(int width, unsigned char *dest, unsigned char *source,
822     IMAGE_DEVICEN *devicen, int devicen_gray)
823 {
824     int i;
825     int cyan, magenta, yellow, black;
826     int vc = devicen[0].visible;
827     int vm = devicen[1].visible;
828     int vy = devicen[2].visible;
829     int vk = devicen[3].visible;
830     int vall = vc && vm && vy && vk;
831     int show_gray = (vc + vm + vy + vk == 1) && devicen_gray;
832     for (i=0; i<width; i++) {
833 	cyan = source[0];
834 	magenta = source[1];
835 	yellow = source[2];
836 	black = source[3];
837 	if (!vall) {
838 	    if (!vc)
839 		cyan = 0;
840 	    if (!vm)
841 		magenta = 0;
842 	    if (!vy)
843 		yellow = 0;
844 	    if (!vk)
845 		black = 0;
846 	    if (show_gray) {
847 		black += cyan + magenta + yellow;
848 		cyan = magenta = yellow = 0;
849 	    }
850 	}
851 	*dest++ = (255 - yellow)  * (255 - black)/255; /* blue */
852 	*dest++ = (255 - magenta) * (255 - black)/255; /* green */
853 	*dest++ = (255 - cyan)    * (255 - black)/255; /* red */
854 	source += 4;
855     }
856 }
857 
858 void
image_devicen_to_24BGR(int width,unsigned char * dest,unsigned char * source,IMAGE_DEVICEN * devicen,int devicen_gray)859 image_devicen_to_24BGR(int width, unsigned char *dest, unsigned char *source,
860     IMAGE_DEVICEN *devicen, int devicen_gray)
861 {
862     int i, j;
863     int cyan, magenta, yellow, black;
864     int num_comp = 0;
865     int value;
866     int num_visible = 0;
867     int show_gray = 0;
868     for (j=0; j<IMAGE_DEVICEN_MAX; j++) {
869 	if (devicen[j].used) {
870 	   num_comp = j+1;
871 	   if (devicen[j].visible)
872 		num_visible++;
873 	}
874     }
875     if ((num_visible == 1) && devicen_gray)
876 	show_gray = 1;
877 
878     for (i=0; i<width; i++) {
879 	cyan = magenta = yellow = black = 0;
880 	for (j=0; j<num_comp; j++) {
881 	    if (devicen[j].visible && devicen[j].used) {
882 		value = source[j];
883 		if (show_gray)
884 		    black += value;
885 		else {
886 		    cyan    += value * devicen[j].cyan    / 65535;
887 		    magenta += value * devicen[j].magenta / 65535;
888 		    yellow  += value * devicen[j].yellow  / 65535;
889 		    black   += value * devicen[j].black / 65535;
890 		}
891 	    }
892 	}
893 	if (cyan > 255)
894 	   cyan = 255;
895 	if (magenta > 255)
896 	   magenta = 255;
897 	if (yellow > 255)
898 	   yellow = 255;
899 	if (black > 255)
900 	   black = 255;
901 	*dest++ = (255 - yellow)  * (255 - black)/255; /* blue */
902 	*dest++ = (255 - magenta) * (255 - black)/255; /* green */
903 	*dest++ = (255 - cyan)    * (255 - black)/255; /* red */
904 	source += 8;
905     }
906 }
907 
908 void
image_convert_line(IMAGE * img,unsigned char * dest,unsigned char * source)909 image_convert_line(IMAGE *img, unsigned char *dest, unsigned char *source)
910 {
911     unsigned char *d = dest;
912     unsigned char *s = source;
913     int width = img->bmih.biWidth;
914     unsigned int alpha = img->format & DISPLAY_ALPHA_MASK;
915     BOOL bigendian = (img->format & DISPLAY_ENDIAN_MASK) == DISPLAY_BIGENDIAN;
916     int i;
917 
918     switch (img->format & DISPLAY_COLORS_MASK) {
919 	case DISPLAY_COLORS_NATIVE:
920 	    if ((img->format & DISPLAY_DEPTH_MASK) == DISPLAY_DEPTH_16) {
921 		if (bigendian) {
922 		    if ((img->format & DISPLAY_555_MASK)
923 			== DISPLAY_NATIVE_555)
924 			image_16RGB555_to_24BGR(img->bmih.biWidth,
925 			    dest, source);
926 		    else
927 			image_16RGB565_to_24BGR(img->bmih.biWidth,
928 			    dest, source);
929 		}
930 		else {
931 		    if ((img->format & DISPLAY_555_MASK)
932 			== DISPLAY_NATIVE_555) {
933 			image_16BGR555_to_24BGR(img->bmih.biWidth,
934 			    dest, source);
935 		    }
936 		    else
937 			image_16BGR565_to_24BGR(img->bmih.biWidth,
938 			    dest, source);
939 		}
940 	    }
941 	    break;
942 	case DISPLAY_COLORS_RGB:
943 	    if ((img->format & DISPLAY_DEPTH_MASK) != DISPLAY_DEPTH_8)
944 		return;
945 	    for (i=0; i<width; i++) {
946 		if ((alpha == DISPLAY_ALPHA_FIRST) ||
947 		    (alpha == DISPLAY_UNUSED_FIRST))
948 		    s++;
949 		if (bigendian) {
950 		    *d++ = s[2];
951 		    *d++ = s[1];
952 		    *d++ = s[0];
953 		    s+=3;
954 		}
955 		else {
956 		    *d++ = *s++;
957 		    *d++ = *s++;
958 		    *d++ = *s++;
959 		}
960 		if ((alpha == DISPLAY_ALPHA_LAST) ||
961 		    (alpha == DISPLAY_UNUSED_LAST))
962 		    s++;
963 	    }
964 /*
965 printf("rgb, width=%d alpha=%d d=0x%x s=0x%x\n", width, alpha, (int)d, (int)s);
966 printf("   d=0x%x s=0x%x\n", (int)d, (int)s);
967 */
968 	    break;
969 	case DISPLAY_COLORS_CMYK:
970 	    if ((img->format & DISPLAY_DEPTH_MASK) == DISPLAY_DEPTH_8)
971 		image_32CMYK_to_24BGR(width, dest, source,
972 		    img->devicen, img->devicen_gray);
973 	    else if ((img->format & DISPLAY_DEPTH_MASK) == DISPLAY_DEPTH_1) {
974 		image_4CMYK_to_24BGR(width, dest, source,
975 		    img->devicen, img->devicen_gray);
976 	    }
977 	    else
978 		return;
979 	    break;
980 	case DISPLAY_COLORS_SEPARATION:
981 	    if ((img->format & DISPLAY_DEPTH_MASK) != DISPLAY_DEPTH_8)
982 		return;
983 	    image_devicen_to_24BGR(width, dest, source,
984 		img->devicen, img->devicen_gray);
985 	    break;
986     }
987 }
988 
989 /* This makes a copy of the bitmap in global memory, suitable for clipboard */
990 /* Do not put 16 or 32-bit per pixels on the clipboard because */
991 /* ClipBook Viewer (NT4) can't display them */
992 static HGLOBAL
copy_dib(IMAGE * img)993 copy_dib(IMAGE *img)
994 {
995     int bitsperpixel;
996     int bytewidth;
997     int bitmapsize;
998     int palcount;
999     HGLOBAL hglobal;
1000     BYTE *pBits;
1001     BYTE *pLine;
1002     BYTE *pDIB;
1003     BITMAPINFOHEADER *pbmih;
1004     RGBQUAD *pColors;
1005     int i;
1006     BOOL directcopy = FALSE;
1007 
1008     /* Allocates memory for the clipboard bitmap */
1009     if (img->bmih.biBitCount <= 1)
1010 	bitsperpixel = 1;
1011     else if (img->bmih.biBitCount <= 4)
1012 	bitsperpixel = 4;
1013     else if (img->bmih.biBitCount <= 8)
1014 	bitsperpixel = 8;
1015     else
1016 	bitsperpixel = 24;
1017     bytewidth = ((img->bmih.biWidth * bitsperpixel + 31 ) & ~31) >> 3;
1018     bitmapsize = bytewidth * img->bmih.biHeight;
1019     if (bitsperpixel > 8)
1020 	palcount = 0;	/* 24-bit BGR */
1021     else
1022 	palcount = img->bmih.biClrUsed;
1023 
1024     hglobal = GlobalAlloc(GHND | GMEM_SHARE, sizeof(BITMAPINFOHEADER)
1025 			  + sizeof(RGBQUAD) * palcount + bitmapsize);
1026     if (hglobal == (HGLOBAL) NULL)
1027 	return (HGLOBAL) NULL;
1028     pDIB = (BYTE *) GlobalLock(hglobal);
1029     if (pDIB == (BYTE *) NULL)
1030 	return (HGLOBAL) NULL;
1031 
1032 
1033     /* initialize the clipboard bitmap */
1034     pbmih = (BITMAPINFOHEADER *) (pDIB);
1035     pColors = (RGBQUAD *) (pDIB + sizeof(BITMAPINFOHEADER));
1036     pBits = (BYTE *) (pDIB + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * palcount);
1037     pbmih->biSize = sizeof(BITMAPINFOHEADER);
1038     pbmih->biWidth = img->bmih.biWidth;
1039     pbmih->biHeight = img->bmih.biHeight;
1040     pbmih->biPlanes = 1;
1041     pbmih->biBitCount = bitsperpixel;
1042     pbmih->biCompression = 0;
1043     pbmih->biSizeImage = 0;	/* default */
1044     pbmih->biXPelsPerMeter = 0;
1045     pbmih->biYPelsPerMeter = 0;
1046     pbmih->biClrUsed = palcount;
1047     pbmih->biClrImportant = palcount;
1048 
1049     for (i = 0; i < palcount; i++) {
1050 	image_color(img->format, i, &pColors[i].rgbRed,
1051 	    &pColors[i].rgbGreen, &pColors[i].rgbBlue);
1052 	pColors[i].rgbReserved = 0;
1053     }
1054 
1055     /* find out if the format needs to be converted */
1056     switch (img->format & DISPLAY_COLORS_MASK) {
1057 	case DISPLAY_COLORS_NATIVE:
1058 	    switch (img->format & DISPLAY_DEPTH_MASK) {
1059 		case DISPLAY_DEPTH_1:
1060 		case DISPLAY_DEPTH_4:
1061 		case DISPLAY_DEPTH_8:
1062 		    directcopy = TRUE;
1063 	    }
1064 	    break;
1065 	case DISPLAY_COLORS_GRAY:
1066 	    switch (img->format & DISPLAY_DEPTH_MASK) {
1067 		case DISPLAY_DEPTH_1:
1068 		case DISPLAY_DEPTH_4:
1069 		case DISPLAY_DEPTH_8:
1070 		    directcopy = TRUE;
1071 	    }
1072 	    break;
1073 	case DISPLAY_COLORS_RGB:
1074 	    if (((img->format & DISPLAY_DEPTH_MASK) == DISPLAY_DEPTH_8) &&
1075 		((img->format & DISPLAY_ALPHA_MASK) == DISPLAY_ALPHA_NONE) &&
1076 		((img->format & DISPLAY_ENDIAN_MASK) == DISPLAY_LITTLEENDIAN))
1077 		directcopy = TRUE;
1078     }
1079 
1080     pLine = pBits;
1081     if (directcopy) {
1082 	for (i = 0; i < img->bmih.biHeight; i++) {
1083 	    memcpy(pLine, img->image + i * img->raster, bytewidth);
1084 	    pLine += bytewidth;
1085 	}
1086     }
1087     else {
1088 	/* we need to convert the format to 24BGR */
1089 	for (i = 0; i < img->bmih.biHeight; i++) {
1090 	    image_convert_line(img, pLine, img->image + i * img->raster);
1091 	    pLine += bytewidth;
1092 	}
1093     }
1094 
1095     GlobalUnlock(hglobal);
1096 
1097     return hglobal;
1098 }
1099 
1100 static HPALETTE
create_palette(IMAGE * img)1101 create_palette(IMAGE *img)
1102 {
1103     int i;
1104     int nColors;
1105     HPALETTE palette = NULL;
1106 
1107     nColors = img->bmih.biClrUsed;
1108     if (nColors) {
1109 	LPLOGPALETTE logpalette;
1110 	logpalette = (LPLOGPALETTE) malloc(sizeof(LOGPALETTE) +
1111 	    nColors * sizeof(PALETTEENTRY));
1112 	if (logpalette == (LPLOGPALETTE) NULL)
1113 	    return (HPALETTE)0;
1114 	logpalette->palVersion = 0x300;
1115 	logpalette->palNumEntries = img->bmih.biClrUsed;
1116 	for (i = 0; i < nColors; i++) {
1117 	    logpalette->palPalEntry[i].peFlags = 0;
1118 	    image_color(img->format, i,
1119 		&logpalette->palPalEntry[i].peRed,
1120 		&logpalette->palPalEntry[i].peGreen,
1121 		&logpalette->palPalEntry[i].peBlue);
1122 	}
1123 	palette = CreatePalette(logpalette);
1124 	free(logpalette);
1125     }
1126     return palette;
1127 }
1128 
1129 /* image window */
1130 /* All accesses to img->image or dimensions must be protected by mutex */
1131 LRESULT CALLBACK
WndImg2Proc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)1132 WndImg2Proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
1133 {
1134     HDC hdc;
1135     PAINTSTRUCT ps;
1136     RECT rect;
1137     int nVscrollInc, nHscrollInc;
1138     IMAGE *img;
1139 
1140     if (message == WM_CREATE) {
1141 	/* Object is stored in window extra data.
1142 	 * Nothing must try to use the object before WM_CREATE
1143 	 * initializes it here.
1144 	 */
1145 	img = (IMAGE *)(((CREATESTRUCT *)lParam)->lpCreateParams);
1146 	SetWindowLong(hwnd, 0, (LONG)img);
1147     }
1148     img = (IMAGE *)GetWindowLong(hwnd, 0);
1149 
1150 
1151     switch(message) {
1152 	case WM_SYSCOMMAND:
1153 	    /* copy to clipboard */
1154 	    if (LOWORD(wParam) == M_COPY_CLIP) {
1155 		HGLOBAL hglobal;
1156 		HPALETTE hpalette;
1157 		if (img->hmutex != INVALID_HANDLE_VALUE)
1158 		    WaitForSingleObject(img->hmutex, 120000);
1159 		hglobal = copy_dib(img);
1160 		if (hglobal == (HGLOBAL)NULL) {
1161 		    if (img->hmutex != INVALID_HANDLE_VALUE)
1162 			ReleaseMutex(img->hmutex);
1163 		    MessageBox(hwnd, "Not enough memory to Copy to Clipboard",
1164 			szImgName2, MB_OK | MB_ICONEXCLAMATION);
1165 		    return 0;
1166 		}
1167 		OpenClipboard(hwnd);
1168 		EmptyClipboard();
1169 		SetClipboardData(CF_DIB, hglobal);
1170 		hpalette = create_palette(img);
1171 		if (hpalette)
1172 		    SetClipboardData(CF_PALETTE, hpalette);
1173 		CloseClipboard();
1174 		if (img->hmutex != INVALID_HANDLE_VALUE)
1175 		    ReleaseMutex(img->hmutex);
1176 		return 0;
1177 	    }
1178 	    else if ((LOWORD(wParam) >= M_SEPARATION) &&
1179 	             (LOWORD(wParam) < M_SEPARATION+IMAGE_DEVICEN_MAX)) {
1180 		sep_menu(img, LOWORD(wParam) - M_SEPARATION);
1181 	    }
1182 	    else if (LOWORD(wParam) == M_DEVICEN_GRAY) {
1183 		img->devicen_gray = !img->devicen_gray;
1184 		CheckMenuItem(GetSystemMenu(img->hwnd, FALSE), M_DEVICEN_GRAY,
1185 		    (img->devicen_gray ? MF_CHECKED : MF_UNCHECKED));
1186 		InvalidateRect(img->hwnd, NULL, 0);
1187 		UpdateWindow(img->hwnd);
1188 	    }
1189 	    break;
1190 	case WM_CREATE:
1191 	    /* enable drag-drop */
1192 	    DragAcceptFiles(hwnd, TRUE);
1193 	    break;
1194 	case WM_MOVE:
1195 	    if (!IsIconic(hwnd) && !IsZoomed(hwnd)) {
1196 		GetWindowRect(hwnd, &rect);
1197 		img->x = rect.left;
1198 		img->y = rect.top;
1199 	    }
1200 	    break;
1201 	case WM_SIZE:
1202 	    if (wParam == SIZE_MINIMIZED)
1203 		    return(0);
1204 
1205 	    /* remember current window size */
1206 	    if (wParam != SIZE_MAXIMIZED) {
1207 		GetWindowRect(hwnd, &rect);
1208 		img->cx = rect.right - rect.left;
1209 		img->cy = rect.bottom - rect.top;
1210 		img->x = rect.left;
1211 		img->y = rect.top;
1212 	    }
1213 
1214 	    if (img->hmutex != INVALID_HANDLE_VALUE)
1215 		WaitForSingleObject(img->hmutex, 120000);
1216 	    img->cyClient = HIWORD(lParam);
1217 	    img->cxClient = LOWORD(lParam);
1218 
1219 	    img->cyAdjust = min(img->bmih.biHeight, img->cyClient) - img->cyClient;
1220 	    img->cyClient += img->cyAdjust;
1221 
1222 	    img->nVscrollMax = max(0, img->bmih.biHeight - img->cyClient);
1223 	    img->nVscrollPos = min(img->nVscrollPos, img->nVscrollMax);
1224 
1225 	    SetScrollRange(hwnd, SB_VERT, 0, img->nVscrollMax, FALSE);
1226 	    SetScrollPos(hwnd, SB_VERT, img->nVscrollPos, TRUE);
1227 
1228 	    img->cxAdjust = min(img->bmih.biWidth,  img->cxClient) - img->cxClient;
1229 	    img->cxClient += img->cxAdjust;
1230 
1231 	    img->nHscrollMax = max(0, img->bmih.biWidth - img->cxClient);
1232 	    img->nHscrollPos = min(img->nHscrollPos, img->nHscrollMax);
1233 
1234 	    SetScrollRange(hwnd, SB_HORZ, 0, img->nHscrollMax, FALSE);
1235 	    SetScrollPos(hwnd, SB_HORZ, img->nHscrollPos, TRUE);
1236 
1237 	    if ((wParam==SIZENORMAL)
1238 		&& (img->cxAdjust!=0 || img->cyAdjust!=0)) {
1239 		GetWindowRect(GetParent(hwnd),&rect);
1240 		MoveWindow(GetParent(hwnd),rect.left,rect.top,
1241 		    rect.right-rect.left+img->cxAdjust,
1242 		    rect.bottom-rect.top+img->cyAdjust, TRUE);
1243 		img->cxAdjust = img->cyAdjust = 0;
1244 	    }
1245 	    if (img->hmutex != INVALID_HANDLE_VALUE)
1246 		ReleaseMutex(img->hmutex);
1247 	    return(0);
1248 	case WM_VSCROLL:
1249 	    switch(LOWORD(wParam)) {
1250 		case SB_TOP:
1251 		    nVscrollInc = -img->nVscrollPos;
1252 		    break;
1253 		case SB_BOTTOM:
1254 		    nVscrollInc = img->nVscrollMax - img->nVscrollPos;
1255 		    break;
1256 		case SB_LINEUP:
1257 		    nVscrollInc = -img->cyClient/16;
1258 		    break;
1259 		case SB_LINEDOWN:
1260 		    nVscrollInc = img->cyClient/16;
1261 		    break;
1262 		case SB_PAGEUP:
1263 		    nVscrollInc = min(-1,-img->cyClient);
1264 		    break;
1265 		case SB_PAGEDOWN:
1266 		    nVscrollInc = max(1,img->cyClient);
1267 		    break;
1268 		case SB_THUMBTRACK:
1269 		case SB_THUMBPOSITION:
1270 		    nVscrollInc = HIWORD(wParam) - img->nVscrollPos;
1271 		    break;
1272 		default:
1273 		    nVscrollInc = 0;
1274 	    }
1275 	    if ((nVscrollInc = max(-img->nVscrollPos,
1276 		min(nVscrollInc, img->nVscrollMax - img->nVscrollPos)))!=0) {
1277 		img->nVscrollPos += nVscrollInc;
1278 		ScrollWindow(hwnd,0,-nVscrollInc,NULL,NULL);
1279 		SetScrollPos(hwnd,SB_VERT,img->nVscrollPos,TRUE);
1280 		UpdateWindow(hwnd);
1281 	    }
1282 	    return(0);
1283 	case WM_HSCROLL:
1284 	    switch(LOWORD(wParam)) {
1285 		case SB_LINEUP:
1286 		    nHscrollInc = -img->cxClient/16;
1287 		    break;
1288 		case SB_LINEDOWN:
1289 		    nHscrollInc = img->cyClient/16;
1290 		    break;
1291 		case SB_PAGEUP:
1292 		    nHscrollInc = min(-1,-img->cxClient);
1293 		    break;
1294 		case SB_PAGEDOWN:
1295 		    nHscrollInc = max(1,img->cxClient);
1296 		    break;
1297 		case SB_THUMBTRACK:
1298 		case SB_THUMBPOSITION:
1299 		    nHscrollInc = HIWORD(wParam) - img->nHscrollPos;
1300 		    break;
1301 		default:
1302 		    nHscrollInc = 0;
1303 	    }
1304 	    if ((nHscrollInc = max(-img->nHscrollPos,
1305 		min(nHscrollInc, img->nHscrollMax - img->nHscrollPos)))!=0) {
1306 		img->nHscrollPos += nHscrollInc;
1307 		ScrollWindow(hwnd,-nHscrollInc,0,NULL,NULL);
1308 		SetScrollPos(hwnd,SB_HORZ,img->nHscrollPos,TRUE);
1309 		UpdateWindow(hwnd);
1310 	    }
1311 	    return(0);
1312 	case WM_KEYDOWN:
1313 	    switch(LOWORD(wParam)) {
1314 		case VK_HOME:
1315 		    SendMessage(hwnd,WM_VSCROLL,SB_TOP,0L);
1316 		    break;
1317 		case VK_END:
1318 		    SendMessage(hwnd,WM_VSCROLL,SB_BOTTOM,0L);
1319 		    break;
1320 		case VK_PRIOR:
1321 		    SendMessage(hwnd,WM_VSCROLL,SB_PAGEUP,0L);
1322 		    break;
1323 		case VK_NEXT:
1324 		    SendMessage(hwnd,WM_VSCROLL,SB_PAGEDOWN,0L);
1325 		    break;
1326 		case VK_UP:
1327 		    SendMessage(hwnd,WM_VSCROLL,SB_LINEUP,0L);
1328 		    break;
1329 		case VK_DOWN:
1330 		    SendMessage(hwnd,WM_VSCROLL,SB_LINEDOWN,0L);
1331 		    break;
1332 		case VK_LEFT:
1333 		    SendMessage(hwnd,WM_HSCROLL,SB_PAGEUP,0L);
1334 		    break;
1335 		case VK_RIGHT:
1336 		    SendMessage(hwnd,WM_HSCROLL,SB_PAGEDOWN,0L);
1337 		    break;
1338 		case VK_RETURN:
1339 		    if (img->hwndtext)
1340 			BringWindowToTop(img->hwndtext);
1341 		    break;
1342 	    }
1343 	    return(0);
1344 	case WM_CHAR:
1345 	    /* send on all characters to text window */
1346 	    if (img->hwndtext)
1347 		SendMessage(img->hwndtext, message, wParam, lParam);
1348 	    else {
1349 		/* assume we have a console */
1350 		INPUT_RECORD ir;
1351 		HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
1352 		DWORD dwWritten = 0;
1353 		DWORD cks = 0;
1354 		ir.EventType = KEY_EVENT;
1355 		ir.Event.KeyEvent.bKeyDown = TRUE;
1356 		ir.Event.KeyEvent.wRepeatCount = lParam & 0xffff;
1357 		ir.Event.KeyEvent.wVirtualKeyCode = VkKeyScan((TCHAR)wParam) & 0xff;
1358 		ir.Event.KeyEvent.wVirtualScanCode =
1359 		    (lParam >> 16) & 0xff;
1360 		ir.Event.KeyEvent.uChar.AsciiChar = wParam;
1361 		if (GetKeyState(VK_CAPITAL))
1362 		   cks |= CAPSLOCK_ON;
1363 		/* ENHANCED_KEY unimplemented */
1364 		if (GetKeyState(VK_LMENU))
1365 		   cks |= LEFT_ALT_PRESSED;
1366 		if (GetKeyState(VK_LCONTROL))
1367 		   cks |= LEFT_CTRL_PRESSED;
1368 		if (GetKeyState(VK_NUMLOCK))
1369 		   cks |= NUMLOCK_ON;
1370 		if (GetKeyState(VK_RMENU))
1371 		   cks |= RIGHT_ALT_PRESSED;
1372 		if (GetKeyState(VK_RCONTROL))
1373 		   cks |= RIGHT_CTRL_PRESSED;
1374 		if (GetKeyState(VK_SCROLL))
1375 		   cks |= SCROLLLOCK_ON;
1376 		if (GetKeyState(VK_SHIFT))
1377 		   cks |= SHIFT_PRESSED;
1378 		ir.Event.KeyEvent.dwControlKeyState = cks;
1379 		if (ir.Event.KeyEvent.uChar.AsciiChar == 3)
1380 		    GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0L);
1381 		else if (hStdin != INVALID_HANDLE_VALUE)
1382 		    WriteConsoleInput(hStdin, &ir, 1, &dwWritten);
1383 	    }
1384 	    return 0;
1385 	case WM_TIMER:
1386 	    img->update_count++;
1387 	    if (img->update_count >= img->update_interval)
1388 		image_update_now(img);
1389 	    return 0;
1390 	case WM_PAINT:
1391 	    {
1392 	    int sx,sy,wx,wy,dx,dy;
1393 	    RECT fillrect;
1394 	    hdc = BeginPaint(hwnd, &ps);
1395 	    if (img->hmutex != INVALID_HANDLE_VALUE)
1396 		WaitForSingleObject(img->hmutex, 120000);
1397 	    SetMapMode(hdc, MM_TEXT);
1398 	    SetBkMode(hdc,OPAQUE);
1399 	    rect = ps.rcPaint;
1400 	    dx = rect.left;	/* destination */
1401 	    dy = rect.top;
1402 	    wx = rect.right-rect.left; /* width */
1403 	    wy = rect.bottom-rect.top;
1404 	    sx = rect.left;	/* source */
1405 	    sy = rect.top;
1406 	    sx += img->nHscrollPos; /* scrollbars */
1407 	    sy += img->nVscrollPos;
1408 	    if (sx+wx > img->bmih.biWidth)
1409 		    wx = img->bmih.biWidth - sx;
1410 	    if (sy+wy > img->bmih.biHeight)
1411 		    wy = img->bmih.biHeight - sy;
1412 
1413 	    draw(img, hdc, dx, dy, wx, wy, sx, sy);
1414 
1415 	    /* fill areas around page */
1416 	    if (rect.right > img->bmih.biWidth) {
1417 		fillrect.top = rect.top;
1418 		fillrect.left = img->bmih.biWidth;
1419 		fillrect.bottom = rect.bottom;
1420 		fillrect.right = rect.right;
1421 		FillRect(hdc, &fillrect, img->hBrush);
1422 	    }
1423 	    if (rect.bottom > img->bmih.biHeight) {
1424 		fillrect.top = img->bmih.biHeight;
1425 		fillrect.left = rect.left;
1426 		fillrect.bottom = rect.bottom;
1427 		fillrect.right = rect.right;
1428 		FillRect(hdc, &fillrect, img->hBrush);
1429 	    }
1430 
1431 	    if (img->hmutex != INVALID_HANDLE_VALUE)
1432 		ReleaseMutex(img->hmutex);
1433 	    EndPaint(hwnd, &ps);
1434 	    return 0;
1435 	    }
1436 	case WM_DROPFILES:
1437 	    if (img->hwndtext)
1438 		SendMessage(img->hwndtext, message, wParam, lParam);
1439 	    else {
1440 		char *szFile;
1441 		int i, cFiles;
1442 		unsigned int Len, error;
1443 		const char *p;
1444 		const char *szDragPre = "\r(";
1445 		const char *szDragPost = ") run\r";
1446 		HDROP hdrop = (HDROP)wParam;
1447 		cFiles = DragQueryFile(hdrop, (UINT)(-1), (LPSTR)NULL, 0);
1448 		for (i=0; i<cFiles; i++) {
1449 		    Len = DragQueryFile(hdrop, i, NULL, 0);
1450 		    szFile = malloc(Len+1);
1451 		    if (szFile != 0) {
1452 			error = DragQueryFile(hdrop, i, szFile, Len+1);
1453 			if (error != 0) {
1454 			    for (p=szDragPre; *p; p++)
1455 				SendMessage(hwnd,WM_CHAR,*p,1L);
1456 			    for (p=szFile; *p; p++) {
1457 				if (*p == '\\')
1458 				    SendMessage(hwnd,WM_CHAR,'/',1L);
1459 				else
1460 				    SendMessage(hwnd,WM_CHAR,*p,1L);
1461 			    }
1462 			    for (p=szDragPost; *p; p++)
1463 				SendMessage(hwnd,WM_CHAR,*p,1L);
1464 			}
1465 			free(szFile);
1466 		    }
1467 		}
1468 		DragFinish(hdrop);
1469 	    }
1470 	    break;
1471 	case WM_DESTROY:
1472 	    {   /* Save the text window size */
1473 		char winposbuf[64];
1474 		sprintf(winposbuf, "%d %d %d %d", img->x, img->y,
1475 		    img->cx, img->cy);
1476 		win_set_reg_value((img->device != NULL ? "Image" : "Tracer"), winposbuf);
1477 	    }
1478 	    DragAcceptFiles(hwnd, FALSE);
1479 	    break;
1480 
1481     }
1482 
1483 	return DefWindowProc(hwnd, message, wParam, lParam);
1484 }
1485 
1486 
1487 /* Repaint a section of the window. */
1488 static void
draw(IMAGE * img,HDC hdc,int dx,int dy,int wx,int wy,int sx,int sy)1489 draw(IMAGE *img, HDC hdc, int dx, int dy, int wx, int wy,
1490 		int sx, int sy)
1491 {
1492     HPALETTE oldpalette;
1493     struct bmi_s {
1494 	BITMAPINFOHEADER h;
1495 	unsigned short pal_index[256];
1496     } bmi;
1497     int i;
1498     UINT which_colors;
1499     unsigned char *line = NULL;
1500     long ny;
1501     unsigned char *bits;
1502     BOOL directcopy = FALSE;
1503 
1504     if (img->device == NULL)
1505         return;
1506 
1507     memset(&bmi.h, 0, sizeof(bmi.h));
1508 
1509     bmi.h.biSize = sizeof(bmi.h);
1510     bmi.h.biWidth = img->bmih.biWidth;
1511     bmi.h.biHeight = wy;
1512     bmi.h.biPlanes = 1;
1513     bmi.h.biBitCount = img->bmih.biBitCount;
1514     bmi.h.biCompression = 0;
1515     bmi.h.biSizeImage = 0;	/* default */
1516     bmi.h.biXPelsPerMeter = 0;	/* default */
1517     bmi.h.biYPelsPerMeter = 0;	/* default */
1518     bmi.h.biClrUsed = img->bmih.biClrUsed;
1519     bmi.h.biClrImportant = img->bmih.biClrImportant;
1520 
1521     if (img->bmih.biClrUsed) {
1522 	/* palette colors */
1523 	for (i = 0; i < img->bmih.biClrUsed; i++)
1524 	    bmi.pal_index[i] = i;
1525 	which_colors = DIB_PAL_COLORS;
1526     }
1527     else if (bmi.h.biBitCount == 16) {
1528 	DWORD* bmi_colors = (DWORD*)(&bmi.pal_index[0]);
1529 	bmi.h.biCompression = BI_BITFIELDS;
1530 	which_colors = DIB_RGB_COLORS;
1531 	if ((img->format & DISPLAY_555_MASK) == DISPLAY_NATIVE_555) {
1532 	    /* 5-5-5 RGB mode */
1533 	    bmi_colors[0] = 0x7c00;
1534 	    bmi_colors[1] = 0x03e0;
1535 	    bmi_colors[2] = 0x001f;
1536 	}
1537 	else {
1538 	    /* 5-6-5 RGB mode */
1539 	    bmi_colors[0] = 0xf800;
1540 	    bmi_colors[1] = 0x07e0;
1541 	    bmi_colors[2] = 0x001f;
1542 	}
1543     }
1544     else if (bmi.h.biBitCount == 32) {
1545 	unsigned int alpha = img->format & DISPLAY_ALPHA_MASK;
1546 	DWORD* bmi_colors = (DWORD*)(&bmi.pal_index[0]);
1547 	bmi.h.biCompression = BI_BITFIELDS;
1548 	which_colors = DIB_RGB_COLORS;
1549 	if ((img->format & DISPLAY_ENDIAN_MASK) == DISPLAY_BIGENDIAN) {
1550 	    if ((alpha == DISPLAY_ALPHA_FIRST) ||
1551 		(alpha == DISPLAY_UNUSED_FIRST)) {
1552 		/* Mac mode */
1553 		bmi_colors[0] = 0x0000ff00;
1554 		bmi_colors[1] = 0x00ff0000;
1555 		bmi_colors[2] = 0xff000000;
1556 	    }
1557 	    else {
1558 		bmi_colors[0] = 0x000000ff;
1559 		bmi_colors[1] = 0x0000ff00;
1560 		bmi_colors[2] = 0x00ff0000;
1561 	    }
1562 	}
1563 	else {
1564 	    if ((alpha == DISPLAY_ALPHA_FIRST) ||
1565 		(alpha == DISPLAY_UNUSED_FIRST)) {
1566 		/* ignore alpha */
1567 		bmi_colors[0] = 0xff000000;
1568 		bmi_colors[1] = 0x00ff0000;
1569 		bmi_colors[2] = 0x0000ff00;
1570 	    }
1571 	    else {
1572 		/* Windows mode */
1573 		/* ignore alpha */
1574 		bmi_colors[0] = 0x00ff0000;
1575 		bmi_colors[1] = 0x0000ff00;
1576 		bmi_colors[2] = 0x000000ff;
1577 	    }
1578 	}
1579     } else {
1580 	bmi.h.biClrUsed = 0;
1581 	bmi.h.biClrImportant = 0;
1582 	which_colors = DIB_RGB_COLORS;
1583     }
1584 
1585     if (img->raster <= 0)
1586 	return;
1587     if (img->bytewidth <= 0)
1588 	return;
1589 
1590     /* Determine if the format is native and we can do a direct copy */
1591     switch (img->format & DISPLAY_COLORS_MASK) {
1592 	case DISPLAY_COLORS_NATIVE:
1593 	    switch (img->format & DISPLAY_DEPTH_MASK) {
1594 		case DISPLAY_DEPTH_1:
1595 		case DISPLAY_DEPTH_4:
1596 		case DISPLAY_DEPTH_8:
1597 		    directcopy = TRUE;
1598 		    break;
1599 		case DISPLAY_DEPTH_16:
1600 		    if ((img->format & DISPLAY_ENDIAN_MASK)
1601 			== DISPLAY_LITTLEENDIAN)
1602 			directcopy = TRUE;
1603 		    break;
1604 	    }
1605 	    break;
1606 	case DISPLAY_COLORS_GRAY:
1607 	    switch (img->format & DISPLAY_DEPTH_MASK) {
1608 		case DISPLAY_DEPTH_1:
1609 		case DISPLAY_DEPTH_4:
1610 		case DISPLAY_DEPTH_8:
1611 		    directcopy = TRUE;
1612 	    }
1613 	    break;
1614 	case DISPLAY_COLORS_RGB:
1615 	    if (((img->format & DISPLAY_DEPTH_MASK) == DISPLAY_DEPTH_8) &&
1616 	        ((img->format & DISPLAY_ENDIAN_MASK) == DISPLAY_LITTLEENDIAN) &&
1617 	        ((img->format & DISPLAY_ALPHA_MASK) == DISPLAY_ALPHA_NONE))
1618 		directcopy = TRUE;	/* BGR24 */
1619 	    if (((img->format & DISPLAY_DEPTH_MASK) == DISPLAY_DEPTH_8) &&
1620 	        ((img->format & DISPLAY_ENDIAN_MASK) == DISPLAY_LITTLEENDIAN) &&
1621 	        ((img->format & DISPLAY_ALPHA_MASK) == DISPLAY_UNUSED_LAST))
1622 		directcopy = TRUE;	/* 32-bit */
1623 	    break;
1624     }
1625 
1626 
1627     if (which_colors == DIB_PAL_COLORS) {
1628 	oldpalette = SelectPalette(hdc, img->palette, FALSE);
1629 	RealizePalette(hdc);
1630     }
1631 
1632 
1633     /*
1634      * Windows apparently limits the size of a single transfer
1635      * to 2 Mb, which can be exceeded on 24-bit displays.
1636      */
1637     ny = 2000000 / img->raster;
1638 
1639     if (img->raster != img->bytewidth)	/* not 32-bit architecture */
1640 	ny = 1;
1641 
1642     /* If color format not native, convert it line by line */
1643     /* This is slow, but these formats aren't normally used */
1644     if (!directcopy) {
1645 	ny = 1;
1646 	line = (unsigned char *)malloc(img->bytewidth);
1647 	if (line == NULL)
1648 	    return;
1649     }
1650 
1651     for (; wy; dy += ny, wy -= ny, sy += ny) {
1652 	ny = min(ny, wy);
1653 	if (directcopy) {
1654 	    bits = img->image + img->raster * (img->bmih.biHeight - (sy + ny));
1655 	}
1656 	else {
1657 	    image_convert_line(img, line,
1658 		img->image + img->raster * (img->bmih.biHeight - (sy + ny)));
1659 	    bits = line;
1660 	}
1661 	SetDIBitsToDevice(hdc, dx, dy, wx, ny, sx, 0, 0, ny, bits,
1662 		  (BITMAPINFO *) & bmi, which_colors);
1663     }
1664 
1665     if (which_colors == DIB_PAL_COLORS)
1666 	SelectPalette(hdc, oldpalette, FALSE);
1667 
1668     if (line)
1669 	free(line);
1670 }
1671 
1672