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