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