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