1/* 2 * tkWinImage.c -- 3 * 4 * This file contains routines for manipulation full-color images. 5 * 6 * Copyright (c) 1995 Sun Microsystems, Inc. 7 * 8 * See the file "license.terms" for information on usage and redistribution 9 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 10 * 11 * RCS: @(#) $Id$ 12 */ 13 14#include "tkWinInt.h" 15 16static int DestroyImage(XImage* data); 17static unsigned long ImageGetPixel(XImage *image, int x, int y); 18static int PutPixel(XImage *image, int x, int y, 19 unsigned long pixel); 20 21/* 22 *---------------------------------------------------------------------- 23 * 24 * DestroyImage -- 25 * 26 * This is a trivial wrapper around ckfree to make it possible to pass 27 * ckfree as a pointer. 28 * 29 * Results: 30 * None. 31 * 32 * Side effects: 33 * Deallocates the image. 34 * 35 *---------------------------------------------------------------------- 36 */ 37 38static int 39DestroyImage( 40 XImage *imagePtr) /* Image to free. */ 41{ 42 if (imagePtr) { 43 if (imagePtr->data) { 44 ckfree((char*)imagePtr->data); 45 } 46 ckfree((char*)imagePtr); 47 } 48 return 0; 49} 50 51/* 52 *---------------------------------------------------------------------- 53 * 54 * ImageGetPixel -- 55 * 56 * Get a single pixel from an image. 57 * 58 * Results: 59 * Returns the 32 bit pixel value. 60 * 61 * Side effects: 62 * None. 63 * 64 *---------------------------------------------------------------------- 65 */ 66 67static unsigned long 68ImageGetPixel( 69 XImage *image, 70 int x, int y) 71{ 72 unsigned long pixel = 0; 73 unsigned char *srcPtr = &(image->data[(y * image->bytes_per_line) 74 + ((x * image->bits_per_pixel) / NBBY)]); 75 76 switch (image->bits_per_pixel) { 77 case 32: 78 case 24: 79 pixel = RGB(srcPtr[2], srcPtr[1], srcPtr[0]); 80 break; 81 case 16: 82 pixel = RGB(((((WORD*)srcPtr)[0]) >> 7) & 0xf8, 83 ((((WORD*)srcPtr)[0]) >> 2) & 0xf8, 84 ((((WORD*)srcPtr)[0]) << 3) & 0xf8); 85 break; 86 case 8: 87 pixel = srcPtr[0]; 88 break; 89 case 4: 90 pixel = ((x%2) ? (*srcPtr) : ((*srcPtr) >> 4)) & 0x0f; 91 break; 92 case 1: 93 pixel = ((*srcPtr) & (0x80 >> (x%8))) ? 1 : 0; 94 break; 95 } 96 return pixel; 97} 98 99/* 100 *---------------------------------------------------------------------- 101 * 102 * PutPixel -- 103 * 104 * Set a single pixel in an image. 105 * 106 * Results: 107 * None. 108 * 109 * Side effects: 110 * None. 111 * 112 *---------------------------------------------------------------------- 113 */ 114 115static int 116PutPixel( 117 XImage *image, 118 int x, int y, 119 unsigned long pixel) 120{ 121 unsigned char *destPtr = &(image->data[(y * image->bytes_per_line) 122 + ((x * image->bits_per_pixel) / NBBY)]); 123 124 switch (image->bits_per_pixel) { 125 case 32: 126 /* 127 * Pixel is DWORD: 0x00BBGGRR 128 */ 129 130 destPtr[3] = 0; 131 case 24: 132 /* 133 * Pixel is triplet: 0xBBGGRR. 134 */ 135 136 destPtr[0] = (unsigned char) GetBValue(pixel); 137 destPtr[1] = (unsigned char) GetGValue(pixel); 138 destPtr[2] = (unsigned char) GetRValue(pixel); 139 break; 140 case 16: 141 /* 142 * Pixel is WORD: 5-5-5 (R-G-B) 143 */ 144 145 (*(WORD*)destPtr) = ((GetRValue(pixel) & 0xf8) << 7) 146 | ((GetGValue(pixel) & 0xf8) <<2) 147 | ((GetBValue(pixel) & 0xf8) >> 3); 148 break; 149 case 8: 150 /* 151 * Pixel is 8-bit index into color table. 152 */ 153 154 (*destPtr) = (unsigned char) pixel; 155 break; 156 case 4: 157 /* 158 * Pixel is 4-bit index in MSBFirst order. 159 */ 160 161 if (x%2) { 162 (*destPtr) = (unsigned char) (((*destPtr) & 0xf0) 163 | (pixel & 0x0f)); 164 } else { 165 (*destPtr) = (unsigned char) (((*destPtr) & 0x0f) 166 | ((pixel << 4) & 0xf0)); 167 } 168 break; 169 case 1: { 170 /* 171 * Pixel is bit in MSBFirst order. 172 */ 173 174 int mask = (0x80 >> (x%8)); 175 176 if (pixel) { 177 (*destPtr) |= mask; 178 } else { 179 (*destPtr) &= ~mask; 180 } 181 break; 182 } 183 } 184 return 0; 185} 186 187/* 188 *---------------------------------------------------------------------- 189 * 190 * XCreateImage -- 191 * 192 * Allocates storage for a new XImage. 193 * 194 * Results: 195 * Returns a newly allocated XImage. 196 * 197 * Side effects: 198 * None. 199 * 200 *---------------------------------------------------------------------- 201 */ 202 203XImage * 204XCreateImage( 205 Display *display, 206 Visual *visual, 207 unsigned int depth, 208 int format, 209 int offset, 210 char *data, 211 unsigned int width, 212 unsigned int height, 213 int bitmap_pad, 214 int bytes_per_line) 215{ 216 XImage* imagePtr = (XImage *) ckalloc(sizeof(XImage)); 217 imagePtr->width = width; 218 imagePtr->height = height; 219 imagePtr->xoffset = offset; 220 imagePtr->format = format; 221 imagePtr->data = data; 222 imagePtr->byte_order = LSBFirst; 223 imagePtr->bitmap_unit = 8; 224 imagePtr->bitmap_bit_order = LSBFirst; 225 imagePtr->bitmap_pad = bitmap_pad; 226 imagePtr->bits_per_pixel = depth; 227 imagePtr->depth = depth; 228 229 /* 230 * Under Windows, bitmap_pad must be on an LONG data-type boundary. 231 */ 232 233#define LONGBITS (sizeof(LONG) * 8) 234 235 bitmap_pad = (bitmap_pad + LONGBITS - 1) / LONGBITS * LONGBITS; 236 237 /* 238 * Round to the nearest bitmap_pad boundary. 239 */ 240 241 if (bytes_per_line) { 242 imagePtr->bytes_per_line = bytes_per_line; 243 } else { 244 imagePtr->bytes_per_line = (((depth * width) 245 + (bitmap_pad - 1)) >> 3) & ~((bitmap_pad >> 3) - 1); 246 } 247 248 imagePtr->red_mask = 0; 249 imagePtr->green_mask = 0; 250 imagePtr->blue_mask = 0; 251 252 imagePtr->f.put_pixel = PutPixel; 253 imagePtr->f.get_pixel = ImageGetPixel; 254 imagePtr->f.destroy_image = DestroyImage; 255 imagePtr->f.create_image = NULL; 256 imagePtr->f.sub_image = NULL; 257 imagePtr->f.add_pixel = NULL; 258 259 return imagePtr; 260} 261 262/* 263 *---------------------------------------------------------------------- 264 * 265 * XGetImageZPixmap -- 266 * 267 * This function copies data from a pixmap or window into an XImage. This 268 * handles the ZPixmap case only. 269 * 270 * Results: 271 * Returns a newly allocated image containing the data from the given 272 * rectangle of the given drawable. 273 * 274 * Side effects: 275 * None. 276 * 277 * This procedure is adapted from the XGetImage implementation in TkNT. That 278 * code is Copyright (c) 1994 Software Research Associates, Inc. 279 * 280 *---------------------------------------------------------------------- 281 */ 282 283static XImage * 284XGetImageZPixmap( 285 Display *display, 286 Drawable d, 287 int x, int y, 288 unsigned int width, unsigned int height, 289 unsigned long plane_mask, 290 int format) 291{ 292 TkWinDrawable *twdPtr = (TkWinDrawable *)d; 293 XImage *ret_image; 294 HDC hdc, hdcMem; 295 HBITMAP hbmp, hbmpPrev; 296 BITMAPINFO *bmInfo = NULL; 297 HPALETTE hPal, hPalPrev1 = 0, hPalPrev2 = 0; 298 int size; 299 unsigned int n; 300 unsigned int depth; 301 unsigned char *data; 302 TkWinDCState state; 303 BOOL ret; 304 305 if (format != ZPixmap) { 306 TkpDisplayWarning( 307 "XGetImageZPixmap: only ZPixmap types are implemented", 308 "XGetImageZPixmap Failure"); 309 return NULL; 310 } 311 312 hdc = TkWinGetDrawableDC(display, d, &state); 313 314 /* 315 * Need to do a Blt operation to copy into a new bitmap. 316 */ 317 318 hbmp = CreateCompatibleBitmap(hdc, (int) width, (int) height); 319 hdcMem = CreateCompatibleDC(hdc); 320 hbmpPrev = SelectObject(hdcMem, hbmp); 321 hPal = state.palette; 322 if (hPal) { 323 hPalPrev1 = SelectPalette(hdcMem, hPal, FALSE); 324 n = RealizePalette(hdcMem); 325 if (n > 0) { 326 UpdateColors(hdcMem); 327 } 328 hPalPrev2 = SelectPalette(hdc, hPal, FALSE); 329 n = RealizePalette(hdc); 330 if (n > 0) { 331 UpdateColors(hdc); 332 } 333 } 334 335 ret = BitBlt(hdcMem, 0, 0, (int) width, (int) height, hdc, x, y, SRCCOPY); 336 if (hPal) { 337 SelectPalette(hdc, hPalPrev2, FALSE); 338 } 339 SelectObject(hdcMem, hbmpPrev); 340 TkWinReleaseDrawableDC(d, hdc, &state); 341 if (ret == FALSE) { 342 ret_image = NULL; 343 goto cleanup; 344 } 345 if (twdPtr->type == TWD_WINDOW) { 346 depth = Tk_Depth((Tk_Window) twdPtr->window.winPtr); 347 } else { 348 depth = twdPtr->bitmap.depth; 349 } 350 351 size = sizeof(BITMAPINFO); 352 if (depth <= 8) { 353 size += sizeof(unsigned short) * (1 << depth); 354 } 355 bmInfo = (BITMAPINFO *) ckalloc((unsigned)size); 356 357 bmInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 358 bmInfo->bmiHeader.biWidth = width; 359 bmInfo->bmiHeader.biHeight = -(int) height; 360 bmInfo->bmiHeader.biPlanes = 1; 361 bmInfo->bmiHeader.biBitCount = depth; 362 bmInfo->bmiHeader.biCompression = BI_RGB; 363 bmInfo->bmiHeader.biSizeImage = 0; 364 bmInfo->bmiHeader.biXPelsPerMeter = 0; 365 bmInfo->bmiHeader.biYPelsPerMeter = 0; 366 bmInfo->bmiHeader.biClrUsed = 0; 367 bmInfo->bmiHeader.biClrImportant = 0; 368 369 if (depth == 1) { 370 unsigned char *p, *pend; 371 372 GetDIBits(hdcMem, hbmp, 0, height, NULL, bmInfo, DIB_PAL_COLORS); 373 data = ckalloc(bmInfo->bmiHeader.biSizeImage); 374 if (!data) { 375 /* printf("Failed to allocate data area for XImage.\n"); */ 376 ret_image = NULL; 377 goto cleanup; 378 } 379 ret_image = XCreateImage(display, NULL, depth, ZPixmap, 0, data, 380 width, height, 32, (int) ((width + 31) >> 3) & ~1); 381 if (ret_image == NULL) { 382 ckfree(data); 383 goto cleanup; 384 } 385 386 /* 387 * Get the BITMAP info into the Image. 388 */ 389 390 if (GetDIBits(hdcMem, hbmp, 0, height, data, bmInfo, 391 DIB_PAL_COLORS) == 0) { 392 ckfree((char *) ret_image->data); 393 ckfree((char *) ret_image); 394 ret_image = NULL; 395 goto cleanup; 396 } 397 p = data; 398 pend = data + bmInfo->bmiHeader.biSizeImage; 399 while (p < pend) { 400 *p = ~*p; 401 p++; 402 } 403 } else if (depth == 8) { 404 unsigned short *palette; 405 unsigned int i; 406 unsigned char *p; 407 408 GetDIBits(hdcMem, hbmp, 0, height, NULL, bmInfo, DIB_PAL_COLORS); 409 data = ckalloc(bmInfo->bmiHeader.biSizeImage); 410 if (!data) { 411 /* printf("Failed to allocate data area for XImage.\n"); */ 412 ret_image = NULL; 413 goto cleanup; 414 } 415 ret_image = XCreateImage(display, NULL, 8, ZPixmap, 0, data, 416 width, height, 8, (int) width); 417 if (ret_image == NULL) { 418 ckfree((char *) data); 419 goto cleanup; 420 } 421 422 /* 423 * Get the BITMAP info into the Image. 424 */ 425 426 if (GetDIBits(hdcMem, hbmp, 0, height, data, bmInfo, 427 DIB_PAL_COLORS) == 0) { 428 ckfree((char *) ret_image->data); 429 ckfree((char *) ret_image); 430 ret_image = NULL; 431 goto cleanup; 432 } 433 p = data; 434 palette = (unsigned short *) bmInfo->bmiColors; 435 for (i = 0; i < bmInfo->bmiHeader.biSizeImage; i++, p++) { 436 *p = (unsigned char) palette[*p]; 437 } 438 } else if (depth == 16) { 439 GetDIBits(hdcMem, hbmp, 0, height, NULL, bmInfo, DIB_RGB_COLORS); 440 data = ckalloc(bmInfo->bmiHeader.biSizeImage); 441 if (!data) { 442 /* printf("Failed to allocate data area for XImage.\n"); */ 443 ret_image = NULL; 444 goto cleanup; 445 } 446 ret_image = XCreateImage(display, NULL, 16, ZPixmap, 0, data, 447 width, height, 16, 0 /* will be calc'ed from bitmap_pad */); 448 if (ret_image == NULL) { 449 ckfree((char *) data); 450 goto cleanup; 451 } 452 453 /* 454 * Get the BITMAP info directly into the Image. 455 */ 456 457 if (GetDIBits(hdcMem, hbmp, 0, height, ret_image->data, bmInfo, 458 DIB_RGB_COLORS) == 0) { 459 ckfree((char *) ret_image->data); 460 ckfree((char *) ret_image); 461 ret_image = NULL; 462 goto cleanup; 463 } 464 } else { 465 GetDIBits(hdcMem, hbmp, 0, height, NULL, bmInfo, DIB_RGB_COLORS); 466 data = ckalloc(width * height * 4); 467 if (!data) { 468 /* printf("Failed to allocate data area for XImage.\n"); */ 469 ret_image = NULL; 470 goto cleanup; 471 } 472 ret_image = XCreateImage(display, NULL, 32, ZPixmap, 0, data, 473 width, height, 0, (int) width * 4); 474 if (ret_image == NULL) { 475 ckfree((char *) data); 476 goto cleanup; 477 } 478 479 if (depth <= 24) { 480 /* 481 * This used to handle 16 and 24 bpp, but now just handles 24. It 482 * can likely be optimized for that. -- hobbs 483 */ 484 485 unsigned char *smallBitData, *smallBitBase, *bigBitData; 486 unsigned int byte_width, h, w; 487 488 byte_width = ((width * 3 + 3) & ~(unsigned)3); 489 smallBitBase = ckalloc(byte_width * height); 490 if (!smallBitBase) { 491 ckfree((char *) ret_image->data); 492 ckfree((char *) ret_image); 493 ret_image = NULL; 494 goto cleanup; 495 } 496 smallBitData = smallBitBase; 497 498 /* 499 * Get the BITMAP info into the Image. 500 */ 501 502 if (GetDIBits(hdcMem, hbmp, 0, height, smallBitData, bmInfo, 503 DIB_RGB_COLORS) == 0) { 504 ckfree((char *) ret_image->data); 505 ckfree((char *) ret_image); 506 ckfree((char *) smallBitBase); 507 ret_image = NULL; 508 goto cleanup; 509 } 510 511 /* 512 * Copy the 24 Bit Pixmap to a 32-Bit one. 513 */ 514 515 for (h = 0; h < height; h++) { 516 bigBitData = ret_image->data + h * ret_image->bytes_per_line; 517 smallBitData = smallBitBase + h * byte_width; 518 519 for (w = 0; w < width; w++) { 520 *bigBitData++ = ((*smallBitData++)); 521 *bigBitData++ = ((*smallBitData++)); 522 *bigBitData++ = ((*smallBitData++)); 523 *bigBitData++ = 0; 524 } 525 } 526 527 /* 528 * Free the Device contexts, and the Bitmap. 529 */ 530 531 ckfree((char *) smallBitBase); 532 } else { 533 /* 534 * Get the BITMAP info directly into the Image. 535 */ 536 537 if (GetDIBits(hdcMem, hbmp, 0, height, ret_image->data, bmInfo, 538 DIB_RGB_COLORS) == 0) { 539 ckfree((char *) ret_image->data); 540 ckfree((char *) ret_image); 541 ret_image = NULL; 542 goto cleanup; 543 } 544 } 545 } 546 547 cleanup: 548 if (bmInfo) { 549 ckfree((char *) bmInfo); 550 } 551 if (hPal) { 552 SelectPalette(hdcMem, hPalPrev1, FALSE); 553 } 554 DeleteDC(hdcMem); 555 DeleteObject(hbmp); 556 557 return ret_image; 558} 559 560/* 561 *---------------------------------------------------------------------- 562 * 563 * XGetImage -- 564 * 565 * This function copies data from a pixmap or window into an XImage. 566 * 567 * Results: 568 * Returns a newly allocated image containing the data from the given 569 * rectangle of the given drawable. 570 * 571 * Side effects: 572 * None. 573 * 574 *---------------------------------------------------------------------- 575 */ 576 577XImage * 578XGetImage( 579 Display* display, 580 Drawable d, 581 int x, int y, 582 unsigned int width, unsigned int height, 583 unsigned long plane_mask, 584 int format) 585{ 586 TkWinDrawable *twdPtr = (TkWinDrawable *)d; 587 XImage *imagePtr; 588 HDC dc; 589 590 display->request++; 591 592 if (twdPtr == NULL) { 593 /* 594 * Avoid unmapped windows or bad drawables 595 */ 596 597 return NULL; 598 } 599 600 if (twdPtr->type != TWD_BITMAP) { 601 /* 602 * This handles TWD_WINDOW or TWD_WINDC, always creating a 32bit 603 * image. If the window being copied isn't visible (unmapped or 604 * obscured), we quietly stop copying (no user error). The user will 605 * see black where the widget should be. This branch is likely 606 * followed in favor of XGetImageZPixmap as postscript printed widgets 607 * require RGB data. 608 */ 609 610 TkWinDCState state; 611 unsigned int xx, yy, size; 612 COLORREF pixel; 613 614 dc = TkWinGetDrawableDC(display, d, &state); 615 616 imagePtr = XCreateImage(display, NULL, 32, format, 0, NULL, 617 width, height, 32, 0); 618 size = imagePtr->bytes_per_line * imagePtr->height; 619 imagePtr->data = ckalloc(size); 620 ZeroMemory(imagePtr->data, size); 621 622 for (yy = 0; yy < height; yy++) { 623 for (xx = 0; xx < width; xx++) { 624 pixel = GetPixel(dc, x+(int)xx, y+(int)yy); 625 if (pixel == CLR_INVALID) { 626 break; 627 } 628 PutPixel(imagePtr, (int) xx, (int) yy, pixel); 629 } 630 } 631 632 TkWinReleaseDrawableDC(d, dc, &state); 633 } else if (format == ZPixmap) { 634 /* 635 * This actually handles most TWD_WINDOW requests, but it varies from 636 * the above in that it really does a screen capture of an area, which 637 * is consistent with the Unix behavior, but does not appear to handle 638 * all bit depths correctly. -- hobbs 639 */ 640 641 imagePtr = XGetImageZPixmap(display, d, x, y, 642 width, height, plane_mask, format); 643 } else { 644 char *errMsg = NULL; 645 char infoBuf[sizeof(BITMAPINFO) + sizeof(RGBQUAD)]; 646 BITMAPINFO *infoPtr = (BITMAPINFO*)infoBuf; 647 648 if (twdPtr->bitmap.handle == NULL) { 649 errMsg = "XGetImage: not implemented for empty bitmap handles"; 650 } else if (format != XYPixmap) { 651 errMsg = "XGetImage: not implemented for format != XYPixmap"; 652 } else if (plane_mask != 1) { 653 errMsg = "XGetImage: not implemented for plane_mask != 1"; 654 } 655 if (errMsg != NULL) { 656 /* 657 * Do a soft warning for the unsupported XGetImage types. 658 */ 659 660 TkpDisplayWarning(errMsg, "XGetImage Failure"); 661 return NULL; 662 } 663 664 imagePtr = XCreateImage(display, NULL, 1, XYBitmap, 0, NULL, 665 width, height, 32, 0); 666 imagePtr->data = 667 ckalloc((unsigned) imagePtr->bytes_per_line*imagePtr->height); 668 669 dc = GetDC(NULL); 670 671 GetDIBits(dc, twdPtr->bitmap.handle, 0, height, NULL, 672 infoPtr, DIB_RGB_COLORS); 673 674 infoPtr->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 675 infoPtr->bmiHeader.biWidth = width; 676 infoPtr->bmiHeader.biHeight = -(LONG)height; 677 infoPtr->bmiHeader.biPlanes = 1; 678 infoPtr->bmiHeader.biBitCount = 1; 679 infoPtr->bmiHeader.biCompression = BI_RGB; 680 infoPtr->bmiHeader.biSizeImage = 0; 681 infoPtr->bmiHeader.biXPelsPerMeter = 0; 682 infoPtr->bmiHeader.biYPelsPerMeter = 0; 683 infoPtr->bmiHeader.biClrUsed = 0; 684 infoPtr->bmiHeader.biClrImportant = 0; 685 686 GetDIBits(dc, twdPtr->bitmap.handle, 0, height, imagePtr->data, 687 infoPtr, DIB_RGB_COLORS); 688 ReleaseDC(NULL, dc); 689 } 690 691 return imagePtr; 692} 693 694/* 695 * Local Variables: 696 * mode: c 697 * c-basic-offset: 4 698 * fill-column: 78 699 * End: 700 */ 701