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: tkWinImage.c,v 1.6.2.2 2003/10/29 01:08:02 hobbs Exp $ 12 */ 13 14#include "tkWinInt.h" 15 16static int DestroyImage _ANSI_ARGS_((XImage* data)); 17static unsigned long ImageGetPixel _ANSI_ARGS_((XImage *image, int x, int y)); 18static int PutPixel _ANSI_ARGS_((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 27 * pass ckfree as a pointer. 28 * 29 * Results: 30 * None. 31 * 32 * Side effects: 33 * Deallocates the image. 34 * 35 *---------------------------------------------------------------------- 36 */ 37 38static int 39DestroyImage(imagePtr) 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(image, x, y) 69 XImage *image; 70 int x, 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(image, x, y, pixel) 117 XImage *image; 118 int x, 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) = 146 ((GetRValue(pixel) & 0xf8) << 7) 147 | ((GetGValue(pixel) & 0xf8) <<2) 148 | ((GetBValue(pixel) & 0xf8) >> 3); 149 break; 150 case 8: 151 /* 152 * Pixel is 8-bit index into color table. 153 */ 154 155 (*destPtr) = (unsigned char) pixel; 156 break; 157 case 4: 158 /* 159 * Pixel is 4-bit index in MSBFirst order. 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 if (pixel) { 176 (*destPtr) |= mask; 177 } else { 178 (*destPtr) &= ~mask; 179 } 180 } 181 break; 182 } 183 return 0; 184} 185 186/* 187 *---------------------------------------------------------------------- 188 * 189 * XCreateImage -- 190 * 191 * Allocates storage for a new XImage. 192 * 193 * Results: 194 * Returns a newly allocated XImage. 195 * 196 * Side effects: 197 * None. 198 * 199 *---------------------------------------------------------------------- 200 */ 201 202XImage * 203XCreateImage(display, visual, depth, format, offset, data, width, height, 204 bitmap_pad, bytes_per_line) 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 * XGetImageZPixmap -- 265 * 266 * This function copies data from a pixmap or window into an 267 * XImage. This handles the ZPixmap case only. 268 * 269 * Results: 270 * Returns a newly allocated image containing the data from the 271 * given rectangle of the given drawable. 272 * 273 * Side effects: 274 * None. 275 * 276 * This procedure is adapted from the XGetImage implementation in TkNT. 277 * That code is Copyright (c) 1994 Software Research Associates, Inc. 278 * 279 *---------------------------------------------------------------------- 280 */ 281 282static XImage * 283XGetImageZPixmap(display, d, x, y, width, height, plane_mask, format) 284 Display* display; 285 Drawable d; 286 int x; 287 int y; 288 unsigned int width; 289 unsigned int height; 290 unsigned long plane_mask; 291 int format; 292{ 293 TkWinDrawable *twdPtr = (TkWinDrawable *)d; 294 XImage *ret_image; 295 HDC hdc, hdcMem; 296 HBITMAP hbmp, hbmpPrev; 297 BITMAPINFO *bmInfo = NULL; 298 HPALETTE hPal, hPalPrev1, hPalPrev2; 299 int size; 300 unsigned int n; 301 unsigned int depth; 302 unsigned char *data; 303 TkWinDCState state; 304 BOOL ret; 305 306 if (format != ZPixmap) { 307 TkpDisplayWarning( 308 "XGetImageZPixmap: only ZPixmap types are implemented", 309 "XGetImageZPixmap Failure"); 310 return NULL; 311 } 312 313 hdc = TkWinGetDrawableDC(display, d, &state); 314 315 /* Need to do a Blt operation to copy into a new bitmap */ 316 hbmp = CreateCompatibleBitmap(hdc, width, height); 317 hdcMem = CreateCompatibleDC(hdc); 318 hbmpPrev = SelectObject(hdcMem, hbmp); 319 hPal = state.palette; 320 if (hPal) { 321 hPalPrev1 = SelectPalette(hdcMem, hPal, FALSE); 322 n = RealizePalette(hdcMem); 323 if (n > 0) { 324 UpdateColors (hdcMem); 325 } 326 hPalPrev2 = SelectPalette(hdc, hPal, FALSE); 327 n = RealizePalette(hdc); 328 if (n > 0) { 329 UpdateColors (hdc); 330 } 331 } 332 333 ret = BitBlt(hdcMem, 0, 0, width, height, hdc, x, y, SRCCOPY); 334 if (hPal) { 335 SelectPalette(hdc, hPalPrev2, FALSE); 336 } 337 SelectObject(hdcMem, hbmpPrev); 338 TkWinReleaseDrawableDC(d, hdc, &state); 339 if (ret == FALSE) { 340 goto cleanup; 341 } 342 if (twdPtr->type == TWD_WINDOW) { 343 depth = Tk_Depth((Tk_Window) twdPtr->window.winPtr); 344 } else { 345 depth = twdPtr->bitmap.depth; 346 } 347 348 size = sizeof(BITMAPINFO); 349 if (depth <= 8) { 350 size += sizeof(unsigned short) * (1 << depth); 351 } 352 bmInfo = (BITMAPINFO *) ckalloc(size); 353 354 bmInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 355 bmInfo->bmiHeader.biWidth = width; 356 bmInfo->bmiHeader.biHeight = -(int) height; 357 bmInfo->bmiHeader.biPlanes = 1; 358 bmInfo->bmiHeader.biBitCount = depth; 359 bmInfo->bmiHeader.biCompression = BI_RGB; 360 bmInfo->bmiHeader.biSizeImage = 0; 361 bmInfo->bmiHeader.biXPelsPerMeter = 0; 362 bmInfo->bmiHeader.biYPelsPerMeter = 0; 363 bmInfo->bmiHeader.biClrUsed = 0; 364 bmInfo->bmiHeader.biClrImportant = 0; 365 366 if (depth == 1) { 367 unsigned char *p, *pend; 368 GetDIBits(hdcMem, hbmp, 0, height, NULL, bmInfo, DIB_PAL_COLORS); 369 data = ckalloc(bmInfo->bmiHeader.biSizeImage); 370 if (!data) { 371 /* printf("Failed to allocate data area for XImage.\n"); */ 372 ret_image = NULL; 373 goto cleanup; 374 } 375 ret_image = XCreateImage(display, NULL, depth, ZPixmap, 0, data, 376 width, height, 32, ((width + 31) >> 3) & ~1); 377 if (ret_image == NULL) { 378 ckfree(data); 379 goto cleanup; 380 } 381 382 /* Get the BITMAP info into the Image. */ 383 if (GetDIBits(hdcMem, hbmp, 0, height, data, bmInfo, 384 DIB_PAL_COLORS) == 0) { 385 ckfree((char *) ret_image->data); 386 ckfree((char *) ret_image); 387 ret_image = NULL; 388 goto cleanup; 389 } 390 p = data; 391 pend = data + bmInfo->bmiHeader.biSizeImage; 392 while (p < pend) { 393 *p = ~*p; 394 p++; 395 } 396 } else if (depth == 8) { 397 unsigned short *palette; 398 unsigned int i; 399 unsigned char *p; 400 401 GetDIBits(hdcMem, hbmp, 0, height, NULL, bmInfo, DIB_PAL_COLORS); 402 data = ckalloc(bmInfo->bmiHeader.biSizeImage); 403 if (!data) { 404 /* printf("Failed to allocate data area for XImage.\n"); */ 405 ret_image = NULL; 406 goto cleanup; 407 } 408 ret_image = XCreateImage(display, NULL, 8, ZPixmap, 0, data, 409 width, height, 8, width); 410 if (ret_image == NULL) { 411 ckfree((char *) data); 412 goto cleanup; 413 } 414 415 /* Get the BITMAP info into the Image. */ 416 if (GetDIBits(hdcMem, hbmp, 0, height, data, bmInfo, 417 DIB_PAL_COLORS) == 0) { 418 ckfree((char *) ret_image->data); 419 ckfree((char *) ret_image); 420 ret_image = NULL; 421 goto cleanup; 422 } 423 p = data; 424 palette = (unsigned short *) bmInfo->bmiColors; 425 for (i = 0; i < bmInfo->bmiHeader.biSizeImage; i++, p++) { 426 *p = (unsigned char) palette[*p]; 427 } 428 } else if (depth == 16) { 429 GetDIBits(hdcMem, hbmp, 0, height, NULL, bmInfo, DIB_RGB_COLORS); 430 data = ckalloc(bmInfo->bmiHeader.biSizeImage); 431 if (!data) { 432 /* printf("Failed to allocate data area for XImage.\n"); */ 433 ret_image = NULL; 434 goto cleanup; 435 } 436 ret_image = XCreateImage(display, NULL, 16, ZPixmap, 0, data, 437 width, height, 16, 0 /* will be calc'ed from bitmap_pad */); 438 if (ret_image == NULL) { 439 ckfree((char *) data); 440 goto cleanup; 441 } 442 443 /* Get the BITMAP info directly into the Image. */ 444 if (GetDIBits(hdcMem, hbmp, 0, height, ret_image->data, bmInfo, 445 DIB_RGB_COLORS) == 0) { 446 ckfree((char *) ret_image->data); 447 ckfree((char *) ret_image); 448 ret_image = NULL; 449 goto cleanup; 450 } 451 } else { 452 GetDIBits(hdcMem, hbmp, 0, height, NULL, bmInfo, DIB_RGB_COLORS); 453 data = ckalloc(width * height * 4); 454 if (!data) { 455 /* printf("Failed to allocate data area for XImage.\n"); */ 456 ret_image = NULL; 457 goto cleanup; 458 } 459 ret_image = XCreateImage(display, NULL, 32, ZPixmap, 0, data, 460 width, height, 0, width * 4); 461 if (ret_image == NULL) { 462 ckfree((char *) data); 463 goto cleanup; 464 } 465 466 if (depth <= 24) { 467 /* 468 * This used to handle 16 and 24 bpp, but now just handles 24. 469 * It can likely be optimized for that. -- hobbs 470 */ 471 unsigned char *smallBitData, *smallBitBase, *bigBitData; 472 unsigned int byte_width, h, w; 473 474 byte_width = ((width * 3 + 3) & ~3); 475 smallBitBase = ckalloc(byte_width * height); 476 if (!smallBitBase) { 477 ckfree((char *) ret_image->data); 478 ckfree((char *) ret_image); 479 ret_image = NULL; 480 goto cleanup; 481 } 482 smallBitData = smallBitBase; 483 484 /* Get the BITMAP info into the Image. */ 485 if (GetDIBits(hdcMem, hbmp, 0, height, smallBitData, bmInfo, 486 DIB_RGB_COLORS) == 0) { 487 ckfree((char *) ret_image->data); 488 ckfree((char *) ret_image); 489 ckfree((char *) smallBitBase); 490 ret_image = NULL; 491 goto cleanup; 492 } 493 /* Copy the 24 Bit Pixmap to a 32-Bit one. */ 494 for (h = 0; h < height; h++) { 495 bigBitData = ret_image->data + h * ret_image->bytes_per_line; 496 smallBitData = smallBitBase + h * byte_width; 497 498 for (w = 0; w < width; w++) { 499 *bigBitData++ = ((*smallBitData++)); 500 *bigBitData++ = ((*smallBitData++)); 501 *bigBitData++ = ((*smallBitData++)); 502 *bigBitData++ = 0; 503 } 504 } 505 /* Free the Device contexts, and the Bitmap */ 506 ckfree((char *) smallBitBase); 507 } else { 508 /* Get the BITMAP info directly into the Image. */ 509 if (GetDIBits(hdcMem, hbmp, 0, height, ret_image->data, bmInfo, 510 DIB_RGB_COLORS) == 0) { 511 ckfree((char *) ret_image->data); 512 ckfree((char *) ret_image); 513 ret_image = NULL; 514 goto cleanup; 515 } 516 } 517 } 518 519 cleanup: 520 if (bmInfo) { 521 ckfree((char *) bmInfo); 522 } 523 if (hPal) { 524 SelectPalette(hdcMem, hPalPrev1, FALSE); 525 } 526 DeleteDC(hdcMem); 527 DeleteObject(hbmp); 528 529 return ret_image; 530} 531 532/* 533 *---------------------------------------------------------------------- 534 * 535 * XGetImage -- 536 * 537 * This function copies data from a pixmap or window into an 538 * XImage. 539 * 540 * Results: 541 * Returns a newly allocated image containing the data from the 542 * given rectangle of the given drawable. 543 * 544 * Side effects: 545 * None. 546 * 547 *---------------------------------------------------------------------- 548 */ 549 550XImage * 551XGetImage(display, d, x, y, width, height, plane_mask, format) 552 Display* display; 553 Drawable d; 554 int x; 555 int y; 556 unsigned int width; 557 unsigned int height; 558 unsigned long plane_mask; 559 int format; 560{ 561 TkWinDrawable *twdPtr = (TkWinDrawable *)d; 562 XImage *imagePtr; 563 HDC dc; 564 565 display->request++; 566 567 if (twdPtr == NULL) { 568 /* 569 * Avoid unmapped windows or bad drawables 570 */ 571 return NULL; 572 } 573 574 if (twdPtr->type != TWD_BITMAP) { 575 /* 576 * This handles TWD_WINDOW or TWD_WINDC, always creating a 32bit 577 * image. If the window being copied isn't visible (unmapped or 578 * obscured), we quietly stop copying (no user error). 579 * The user will see black where the widget should be. 580 * This branch is likely followed in favor of XGetImageZPixmap as 581 * postscript printed widgets require RGB data. 582 */ 583 TkWinDCState state; 584 unsigned int xx, yy, size; 585 COLORREF pixel; 586 587 dc = TkWinGetDrawableDC(display, d, &state); 588 589 imagePtr = XCreateImage(display, NULL, 32, 590 format, 0, NULL, width, height, 32, 0); 591 size = imagePtr->bytes_per_line * imagePtr->height; 592 imagePtr->data = ckalloc(size); 593 ZeroMemory(imagePtr->data, size); 594 595 for (yy = 0; yy < height; yy++) { 596 for (xx = 0; xx < width; xx++) { 597 pixel = GetPixel(dc, x+(int)xx, y+(int)yy); 598 if (pixel == CLR_INVALID) { 599 break; 600 } 601 PutPixel(imagePtr, xx, yy, pixel); 602 } 603 } 604 605 TkWinReleaseDrawableDC(d, dc, &state); 606 } else if (format == ZPixmap) { 607 /* 608 * This actually handles most TWD_WINDOW requests, but it varies 609 * from the above in that it really does a screen capture of 610 * an area, which is consistent with the Unix behavior, but does 611 * not appear to handle all bit depths correctly. -- hobbs 612 */ 613 imagePtr = XGetImageZPixmap(display, d, x, y, 614 width, height, plane_mask, format); 615 } else { 616 char *errMsg = NULL; 617 char infoBuf[sizeof(BITMAPINFO) + sizeof(RGBQUAD)]; 618 BITMAPINFO *infoPtr = (BITMAPINFO*)infoBuf; 619 620 if (twdPtr->bitmap.handle == NULL) { 621 errMsg = "XGetImage: not implemented for empty bitmap handles"; 622 } else if (format != XYPixmap) { 623 errMsg = "XGetImage: not implemented for format != XYPixmap"; 624 } else if (plane_mask != 1) { 625 errMsg = "XGetImage: not implemented for plane_mask != 1"; 626 } 627 if (errMsg != NULL) { 628 /* 629 * Do a soft warning for the unsupported XGetImage types. 630 */ 631 TkpDisplayWarning(errMsg, "XGetImage Failure"); 632 return NULL; 633 } 634 635 imagePtr = XCreateImage(display, NULL, 1, XYBitmap, 0, NULL, 636 width, height, 32, 0); 637 imagePtr->data = ckalloc(imagePtr->bytes_per_line * imagePtr->height); 638 639 dc = GetDC(NULL); 640 641 GetDIBits(dc, twdPtr->bitmap.handle, 0, height, NULL, 642 infoPtr, DIB_RGB_COLORS); 643 644 infoPtr->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 645 infoPtr->bmiHeader.biWidth = width; 646 infoPtr->bmiHeader.biHeight = -(LONG)height; 647 infoPtr->bmiHeader.biPlanes = 1; 648 infoPtr->bmiHeader.biBitCount = 1; 649 infoPtr->bmiHeader.biCompression = BI_RGB; 650 infoPtr->bmiHeader.biSizeImage = 0; 651 infoPtr->bmiHeader.biXPelsPerMeter = 0; 652 infoPtr->bmiHeader.biYPelsPerMeter = 0; 653 infoPtr->bmiHeader.biClrUsed = 0; 654 infoPtr->bmiHeader.biClrImportant = 0; 655 656 GetDIBits(dc, twdPtr->bitmap.handle, 0, height, imagePtr->data, 657 infoPtr, DIB_RGB_COLORS); 658 ReleaseDC(NULL, dc); 659 } 660 661 return imagePtr; 662} 663