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