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