xwd.c 33 KB
Newer Older
cristy's avatar
cristy committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%                            X   X  W   W  DDDD                               %
%                             X X   W   W  D   D                              %
%                              X    W   W  D   D                              %
%                             X X   W W W  D   D                              %
%                            X   X   W W   DDDD                               %
%                                                                             %
%                                                                             %
%                Read/Write X Windows System Window Dump Format               %
%                                                                             %
%                              Software Design                                %
cristy's avatar
cristy committed
16
%                                   Cristy                                    %
cristy's avatar
cristy committed
17 18 19
%                                 July 1992                                   %
%                                                                             %
%                                                                             %
Cristy's avatar
Cristy committed
20
%  Copyright 1999-2019 ImageMagick Studio LLC, a non-profit organization      %
cristy's avatar
cristy committed
21 22 23 24 25
%  dedicated to making software imaging solutions freely available.           %
%                                                                             %
%  You may not use this file except in compliance with the License.  You may  %
%  obtain a copy of the License at                                            %
%                                                                             %
Cristy's avatar
Cristy committed
26
%    https://imagemagick.org/script/license.php                               %
cristy's avatar
cristy committed
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
%                                                                             %
%  Unless required by applicable law or agreed to in writing, software        %
%  distributed under the License is distributed on an "AS IS" BASIS,          %
%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
%  See the License for the specific language governing permissions and        %
%  limitations under the License.                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%
*/

/*
  Include declarations.
*/
cristy's avatar
cristy committed
42
#include "MagickCore/studio.h"
Cristy's avatar
Cristy committed
43
#include "MagickCore/attribute.h"
cristy's avatar
cristy committed
44 45 46 47 48 49 50
#include "MagickCore/blob.h"
#include "MagickCore/blob-private.h"
#include "MagickCore/cache.h"
#include "MagickCore/color-private.h"
#include "MagickCore/colormap.h"
#include "MagickCore/colormap-private.h"
#include "MagickCore/colorspace.h"
cristy's avatar
cristy committed
51
#include "MagickCore/colorspace-private.h"
cristy's avatar
cristy committed
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
#include "MagickCore/exception.h"
#include "MagickCore/exception-private.h"
#include "MagickCore/image.h"
#include "MagickCore/image-private.h"
#include "MagickCore/list.h"
#include "MagickCore/magick.h"
#include "MagickCore/memory_.h"
#include "MagickCore/monitor.h"
#include "MagickCore/monitor-private.h"
#include "MagickCore/pixel-accessor.h"
#include "MagickCore/property.h"
#include "MagickCore/quantum-private.h"
#include "MagickCore/static.h"
#include "MagickCore/string_.h"
#include "MagickCore/module.h"
cristy's avatar
cristy committed
67
#if defined(MAGICKCORE_X11_DELEGATE)
cristy's avatar
cristy committed
68
#include "MagickCore/xwindow-private.h"
cristy's avatar
cristy committed
69 70 71 72 73 74 75 76 77 78 79 80
#if !defined(vms)
#include <X11/XWDFile.h>
#else
#include "XWDFile.h"
#endif
#endif

/*
  Forward declarations.
*/
#if defined(MAGICKCORE_X11_DELEGATE)
static MagickBooleanType
cristy's avatar
cristy committed
81
  WriteXWDImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy's avatar
cristy committed
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
#endif

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   I s X W D                                                                 %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  IsXWD() returns MagickTrue if the image format type, identified by the
%  magick string, is XWD.
%
%  The format of the IsXWD method is:
%
%      MagickBooleanType IsXWD(const unsigned char *magick,const size_t length)
%
%  A description of each parameter follows:
%
%    o magick: compare image format pattern against these bytes.
%
%    o length: Specifies the length of the magick string.
%
*/
static MagickBooleanType IsXWD(const unsigned char *magick,const size_t length)
{
  if (length < 8)
    return(MagickFalse);
  if (memcmp(magick+1,"\000\000",2) == 0)
    {
      if (memcmp(magick+4,"\007\000\000",3) == 0)
        return(MagickTrue);
      if (memcmp(magick+5,"\000\000\007",3) == 0)
        return(MagickTrue);
    }
  return(MagickFalse);
}

#if defined(MAGICKCORE_X11_DELEGATE)
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   R e a d X W D I m a g e                                                   %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  ReadXWDImage() reads an X Window System window dump image file and
%  returns it.  It allocates the memory necessary for the new Image structure
%  and returns a pointer to the new image.
%
%  The format of the ReadXWDImage method is:
%
%      Image *ReadXWDImage(const ImageInfo *image_info,ExceptionInfo *exception)
%
%  A description of each parameter follows:
%
%    o image_info: the image info.
%
%    o exception: return any errors or warnings in this structure.
%
*/

static Image *ReadXWDImage(const ImageInfo *image_info,ExceptionInfo *exception)
{
#define CheckOverflowException(length,width,height) \
  (((height) != 0) && ((length)/((size_t) height) != ((size_t) width)))

  char
    *comment;

  Image
    *image;

  int
    x_status;

  MagickBooleanType
    authentic_colormap;

  MagickStatusType
    status;

cristy's avatar
cristy committed
171 172
  Quantum
    index;
cristy's avatar
cristy committed
173

cristy's avatar
cristy committed
174
  register ssize_t
cristy's avatar
cristy committed
175 176
    x;

cristy's avatar
cristy committed
177
  register Quantum
cristy's avatar
cristy committed
178 179
    *q;

cristy's avatar
cristy committed
180
  register ssize_t
cristy's avatar
cristy committed
181 182
    i;

cristy's avatar
cristy committed
183
  register size_t
cristy's avatar
cristy committed
184 185 186
    pixel;

  size_t
187
    length;
cristy's avatar
cristy committed
188 189

  ssize_t
cristy's avatar
cristy committed
190 191
    count,
    y;
cristy's avatar
cristy committed
192

193 194 195
  unsigned long
    lsb_first;

cristy's avatar
cristy committed
196 197 198 199 200 201 202 203 204 205 206 207 208
  XColor
    *colors;

  XImage
    *ximage;

  XWDFileHeader
    header;

  /*
    Open image file.
  */
  assert(image_info != (const ImageInfo *) NULL);
cristy's avatar
cristy committed
209
  assert(image_info->signature == MagickCoreSignature);
cristy's avatar
cristy committed
210 211 212 213
  if (image_info->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
      image_info->filename);
  assert(exception != (ExceptionInfo *) NULL);
cristy's avatar
cristy committed
214
  assert(exception->signature == MagickCoreSignature);
cristy's avatar
cristy committed
215
  image=AcquireImage(image_info,exception);
cristy's avatar
cristy committed
216 217 218 219 220 221 222 223 224 225
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
  if (status == MagickFalse)
    {
      image=DestroyImageList(image);
      return((Image *) NULL);
    }
  /*
     Read in header information.
  */
  count=ReadBlob(image,sz_XWDheader,(unsigned char *) &header);
cristy's avatar
cristy committed
226
  if (count != sz_XWDheader)
cristy's avatar
cristy committed
227 228 229 230 231 232 233 234 235 236 237 238 239
    ThrowReaderException(CorruptImageError,"UnableToReadImageHeader");
  /*
    Ensure the header byte-order is most-significant byte first.
  */
  lsb_first=1;
  if ((int) (*(char *) &lsb_first) != 0)
    MSBOrderLong((unsigned char *) &header,sz_XWDheader);
  /*
    Check to see if the dump file is in the proper format.
  */
  if (header.file_version != XWD_FILE_VERSION)
    ThrowReaderException(CorruptImageError,"FileFormatVersionMismatch");
  if (header.header_size < sz_XWDheader)
Cristy's avatar
Cristy committed
240
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
Cristy's avatar
Cristy committed
241 242
  switch (header.visual_class)
  {
cristy's avatar
cristy committed
243 244
    case StaticGray:
    case GrayScale:
Cristy's avatar
Cristy committed
245 246 247 248 249
    {
      if (header.bits_per_pixel != 1)
        ThrowReaderException(CorruptImageError,"ImproperImageHeader");
      break;
    }
cristy's avatar
cristy committed
250 251
    case StaticColor:
    case PseudoColor:
Cristy's avatar
Cristy committed
252 253
    {
      if ((header.bits_per_pixel < 1) || (header.bits_per_pixel > 15) ||
Cristy's avatar
Cristy committed
254
          (header.ncolors == 0))
Cristy's avatar
Cristy committed
255 256 257
        ThrowReaderException(CorruptImageError,"ImproperImageHeader");
      break;
    }
cristy's avatar
cristy committed
258 259
    case TrueColor:
    case DirectColor:
Cristy's avatar
Cristy committed
260 261 262 263
    {
      if ((header.bits_per_pixel != 16) && (header.bits_per_pixel != 24) &&
          (header.bits_per_pixel != 32))
        ThrowReaderException(CorruptImageError,"ImproperImageHeader");
cristy's avatar
cristy committed
264
      break;
Cristy's avatar
Cristy committed
265
    }
cristy's avatar
cristy committed
266
    default:
Cristy's avatar
Cristy committed
267
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
Cristy's avatar
Cristy committed
268 269 270
  }
  switch (header.pixmap_format)
  {
cristy's avatar
cristy committed
271
    case XYBitmap:
Cristy's avatar
Cristy committed
272 273 274 275 276
    {
      if (header.pixmap_depth != 1)
        ThrowReaderException(CorruptImageError,"ImproperImageHeader");
      break;
    }
cristy's avatar
cristy committed
277 278
    case XYPixmap:
    case ZPixmap:
Cristy's avatar
Cristy committed
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
    {
      if ((header.pixmap_depth < 1) || (header.pixmap_depth > 32))
        ThrowReaderException(CorruptImageError,"ImproperImageHeader");
      switch (header.bitmap_pad)
      {
        case 8:
        case 16:
        case 32:
          break;
        default:
          ThrowReaderException(CorruptImageError,"ImproperImageHeader");
      }
      break;
    }
    default:
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
  }
  switch (header.bitmap_unit)
  {
    case 8:
    case 16:
    case 32:
      break;
    default:
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
  }
  switch (header.byte_order)
  {
    case LSBFirst:
    case MSBFirst:
cristy's avatar
cristy committed
309 310
      break;
    default:
Cristy's avatar
Cristy committed
311
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
cristy's avatar
cristy committed
312
  }
Cristy's avatar
Cristy committed
313 314 315 316 317 318 319 320
  switch (header.bitmap_bit_order)
  {
    case LSBFirst:
    case MSBFirst:
      break;
    default:
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
  }
Cristy's avatar
Cristy committed
321 322
  if (header.ncolors > 65535)
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
Cristy's avatar
Cristy committed
323 324
  if (((header.bitmap_pad % 8) != 0) || (header.bitmap_pad > 32))
    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
Cristy's avatar
Cristy committed
325
  length=(size_t) (header.header_size-sz_XWDheader);
cristy's avatar
cristy committed
326 327 328 329 330
  comment=(char *) AcquireQuantumMemory(length+1,sizeof(*comment));
  if (comment == (char *) NULL)
    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
  count=ReadBlob(image,length,(unsigned char *) comment);
  comment[length]='\0';
cristy's avatar
cristy committed
331
  (void) SetImageProperty(image,"comment",comment,exception);
cristy's avatar
cristy committed
332 333 334 335 336 337
  comment=DestroyString(comment);
  if (count != (ssize_t) length)
    ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
  /*
    Initialize the X image.
  */
cristy's avatar
cristy committed
338
  ximage=(XImage *) AcquireMagickMemory(sizeof(*ximage));
cristy's avatar
cristy committed
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
  if (ximage == (XImage *) NULL)
    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
  ximage->depth=(int) header.pixmap_depth;
  ximage->format=(int) header.pixmap_format;
  ximage->xoffset=(int) header.xoffset;
  ximage->data=(char *) NULL;
  ximage->width=(int) header.pixmap_width;
  ximage->height=(int) header.pixmap_height;
  ximage->bitmap_pad=(int) header.bitmap_pad;
  ximage->bytes_per_line=(int) header.bytes_per_line;
  ximage->byte_order=(int) header.byte_order;
  ximage->bitmap_unit=(int) header.bitmap_unit;
  ximage->bitmap_bit_order=(int) header.bitmap_bit_order;
  ximage->bits_per_pixel=(int) header.bits_per_pixel;
  ximage->red_mask=header.red_mask;
  ximage->green_mask=header.green_mask;
  ximage->blue_mask=header.blue_mask;
Cristy's avatar
Cristy committed
356 357 358
  if ((ximage->width < 0) || (ximage->height < 0) || (ximage->depth < 0) ||
      (ximage->format < 0) || (ximage->byte_order < 0) ||
      (ximage->bitmap_bit_order < 0) || (ximage->bitmap_pad < 0) ||
cristy's avatar
cristy committed
359 360 361 362 363
      (ximage->bytes_per_line < 0))
    {
      ximage=(XImage *) RelinquishMagickMemory(ximage);
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    }
cristy's avatar
cristy committed
364
  if ((ximage->width > 65535) || (ximage->height > 65535))
cristy's avatar
cristy committed
365 366 367 368
    {
      ximage=(XImage *) RelinquishMagickMemory(ximage);
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    }
cristy's avatar
cristy committed
369
  if ((ximage->bits_per_pixel > 32) || (ximage->bitmap_unit > 32))
cristy's avatar
cristy committed
370 371 372 373
    {
      ximage=(XImage *) RelinquishMagickMemory(ximage);
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    }
cristy's avatar
cristy committed
374 375
  x_status=XInitImage(ximage);
  if (x_status == 0)
cristy's avatar
cristy committed
376 377 378 379
    {
      ximage=(XImage *) RelinquishMagickMemory(ximage);
      ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
    }
cristy's avatar
cristy committed
380 381 382 383 384 385 386 387 388 389
  /*
    Read colormap.
  */
  authentic_colormap=MagickFalse;
  colors=(XColor *) NULL;
  if (header.ncolors != 0)
    {
      XWDColor
        color;

Cristy's avatar
Cristy committed
390 391
      colors=(XColor *) AcquireQuantumMemory((size_t) header.ncolors,
        sizeof(*colors));
cristy's avatar
cristy committed
392
      if (colors == (XColor *) NULL)
cristy's avatar
cristy committed
393 394 395 396
        {
          ximage=(XImage *) RelinquishMagickMemory(ximage);
          ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
        }
cristy's avatar
cristy committed
397
      for (i=0; i < (ssize_t) header.ncolors; i++)
cristy's avatar
cristy committed
398 399
      {
        count=ReadBlob(image,sz_XWDColor,(unsigned char *) &color);
cristy's avatar
cristy committed
400
        if (count != sz_XWDColor)
cristy's avatar
cristy committed
401
          {
402
            colors=(XColor *) RelinquishMagickMemory(colors);
cristy's avatar
cristy committed
403 404 405
            ximage=(XImage *) RelinquishMagickMemory(ximage);
            ThrowReaderException(CorruptImageError,"UnexpectedEndOfFile");
          }
cristy's avatar
cristy committed
406 407 408 409 410 411
        colors[i].pixel=color.pixel;
        colors[i].red=color.red;
        colors[i].green=color.green;
        colors[i].blue=color.blue;
        colors[i].flags=(char) color.flags;
        if (color.flags != 0)
cristy's avatar
cristy committed
412
          authentic_colormap=MagickTrue;
cristy's avatar
cristy committed
413 414 415 416 417 418
      }
      /*
        Ensure the header byte-order is most-significant byte first.
      */
      lsb_first=1;
      if ((int) (*(char *) &lsb_first) != 0)
cristy's avatar
cristy committed
419
        for (i=0; i < (ssize_t) header.ncolors; i++)
cristy's avatar
cristy committed
420 421 422 423 424 425 426 427 428 429 430 431
        {
          MSBOrderLong((unsigned char *) &colors[i].pixel,
            sizeof(colors[i].pixel));
          MSBOrderShort((unsigned char *) &colors[i].red,3*
            sizeof(colors[i].red));
        }
    }
  /*
    Allocate the pixel buffer.
  */
  length=(size_t) ximage->bytes_per_line*ximage->height;
  if (CheckOverflowException(length,ximage->bytes_per_line,ximage->height))
cristy's avatar
cristy committed
432
    {
433 434
      if (header.ncolors != 0)
        colors=(XColor *) RelinquishMagickMemory(colors);
cristy's avatar
cristy committed
435 436 437
      ximage=(XImage *) RelinquishMagickMemory(ximage);
      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    }
cristy's avatar
cristy committed
438 439 440 441 442 443 444 445
  if (ximage->format != ZPixmap)
    {
      size_t
        extent;

      extent=length;
      length*=ximage->depth;
      if (CheckOverflowException(length,extent,ximage->depth))
cristy's avatar
cristy committed
446
        {
447 448
          if (header.ncolors != 0)
            colors=(XColor *) RelinquishMagickMemory(colors);
cristy's avatar
cristy committed
449 450 451
          ximage=(XImage *) RelinquishMagickMemory(ximage);
          ThrowReaderException(CorruptImageError,"ImproperImageHeader");
        }
cristy's avatar
cristy committed
452 453 454
    }
  ximage->data=(char *) AcquireQuantumMemory(length,sizeof(*ximage->data));
  if (ximage->data == (char *) NULL)
cristy's avatar
cristy committed
455
    {
456 457
      if (header.ncolors != 0)
        colors=(XColor *) RelinquishMagickMemory(colors);
cristy's avatar
cristy committed
458 459 460
      ximage=(XImage *) RelinquishMagickMemory(ximage);
      ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
    }
cristy's avatar
cristy committed
461
  count=ReadBlob(image,length,(unsigned char *) ximage->data);
cristy's avatar
cristy committed
462
  if (count != (ssize_t) length)
cristy's avatar
cristy committed
463
    {
464 465
      if (header.ncolors != 0)
        colors=(XColor *) RelinquishMagickMemory(colors);
cristy's avatar
cristy committed
466 467
      ximage->data=DestroyString(ximage->data);
      ximage=(XImage *) RelinquishMagickMemory(ximage);
468
      ThrowReaderException(CorruptImageError,"UnableToReadImageData");
cristy's avatar
cristy committed
469
    }
cristy's avatar
cristy committed
470 471 472
  /*
    Convert image to MIFF format.
  */
cristy's avatar
cristy committed
473 474
  image->columns=(size_t) ximage->width;
  image->rows=(size_t) ximage->height;
cristy's avatar
cristy committed
475
  image->depth=8;
cristy's avatar
cristy committed
476 477
  status=SetImageExtent(image,image->columns,image->rows,exception);
  if (status == MagickFalse)
478 479 480 481 482 483 484
    {
      if (header.ncolors != 0)
        colors=(XColor *) RelinquishMagickMemory(colors);
      ximage->data=DestroyString(ximage->data);
      ximage=(XImage *) RelinquishMagickMemory(ximage);
      return(DestroyImageList(image));
    }
cristy's avatar
cristy committed
485
  if ((header.ncolors == 0U) || (ximage->red_mask != 0) ||
cristy's avatar
cristy committed
486 487 488 489 490 491 492 493 494 495 496
      (ximage->green_mask != 0) || (ximage->blue_mask != 0))
    image->storage_class=DirectClass;
  else
    image->storage_class=PseudoClass;
  image->colors=header.ncolors;
  if (image_info->ping == MagickFalse)
    switch (image->storage_class)
    {
      case DirectClass:
      default:
      {
cristy's avatar
cristy committed
497
        register size_t
cristy's avatar
cristy committed
498 499
          color;

cristy's avatar
cristy committed
500
        size_t
cristy's avatar
cristy committed
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535
          blue_mask,
          blue_shift,
          green_mask,
          green_shift,
          red_mask,
          red_shift;

        /*
          Determine shift and mask for red, green, and blue.
        */
        red_mask=ximage->red_mask;
        red_shift=0;
        while ((red_mask != 0) && ((red_mask & 0x01) == 0))
        {
          red_mask>>=1;
          red_shift++;
        }
        green_mask=ximage->green_mask;
        green_shift=0;
        while ((green_mask != 0) && ((green_mask & 0x01) == 0))
        {
          green_mask>>=1;
          green_shift++;
        }
        blue_mask=ximage->blue_mask;
        blue_shift=0;
        while ((blue_mask != 0) && ((blue_mask & 0x01) == 0))
        {
          blue_mask>>=1;
          blue_shift++;
        }
        /*
          Convert X image to DirectClass packets.
        */
        if ((image->colors != 0) && (authentic_colormap != MagickFalse))
cristy's avatar
cristy committed
536
          for (y=0; y < (ssize_t) image->rows; y++)
cristy's avatar
cristy committed
537 538
          {
            q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristy's avatar
cristy committed
539
            if (q == (Quantum *) NULL)
cristy's avatar
cristy committed
540
              break;
cristy's avatar
cristy committed
541
            for (x=0; x < (ssize_t) image->columns; x++)
cristy's avatar
cristy committed
542 543
            {
              pixel=XGetPixel(ximage,(int) x,(int) y);
Cristy's avatar
Cristy committed
544 545
              index=(Quantum) ConstrainColormapIndex(image,(ssize_t) (pixel >>
                red_shift) & red_mask,exception);
Cristy's avatar
Cristy committed
546 547
              SetPixelRed(image,ScaleShortToQuantum(
                colors[(ssize_t) index].red),q);
Cristy's avatar
Cristy committed
548 549
              index=(Quantum) ConstrainColormapIndex(image,(ssize_t) (pixel >>
                green_shift) & green_mask,exception);
Cristy's avatar
Cristy committed
550 551
              SetPixelGreen(image,ScaleShortToQuantum(
                colors[(ssize_t) index].green),q);
Cristy's avatar
Cristy committed
552 553
              index=(Quantum) ConstrainColormapIndex(image,(ssize_t) (pixel >>
                blue_shift) & blue_mask,exception);
Cristy's avatar
Cristy committed
554 555
              SetPixelBlue(image,ScaleShortToQuantum(
                colors[(ssize_t) index].blue),q);
cristy's avatar
cristy committed
556
              q+=GetPixelChannels(image);
cristy's avatar
cristy committed
557 558 559
            }
            if (SyncAuthenticPixels(image,exception) == MagickFalse)
              break;
cristy's avatar
cristy committed
560
            status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy's avatar
cristy committed
561
              image->rows);
cristy's avatar
cristy committed
562 563 564 565
            if (status == MagickFalse)
              break;
          }
        else
cristy's avatar
cristy committed
566
          for (y=0; y < (ssize_t) image->rows; y++)
cristy's avatar
cristy committed
567 568
          {
            q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristy's avatar
cristy committed
569
            if (q == (Quantum *) NULL)
cristy's avatar
cristy committed
570
              break;
cristy's avatar
cristy committed
571
            for (x=0; x < (ssize_t) image->columns; x++)
cristy's avatar
cristy committed
572 573 574
            {
              pixel=XGetPixel(ximage,(int) x,(int) y);
              color=(pixel >> red_shift) & red_mask;
cristy's avatar
cristy committed
575 576 577
              if (red_mask != 0)
                color=(color*65535UL)/red_mask;
              SetPixelRed(image,ScaleShortToQuantum((unsigned short) color),q);
cristy's avatar
cristy committed
578
              color=(pixel >> green_shift) & green_mask;
cristy's avatar
cristy committed
579 580 581 582
              if (green_mask != 0)
                color=(color*65535UL)/green_mask;
              SetPixelGreen(image,ScaleShortToQuantum((unsigned short) color),
                q);
cristy's avatar
cristy committed
583
              color=(pixel >> blue_shift) & blue_mask;
cristy's avatar
cristy committed
584
              if (blue_mask != 0)
cristy's avatar
cristy committed
585
                color=(color*65535UL)/blue_mask;
cristy's avatar
cristy committed
586
              SetPixelBlue(image,ScaleShortToQuantum((unsigned short) color),q);
cristy's avatar
cristy committed
587
              q+=GetPixelChannels(image);
cristy's avatar
cristy committed
588 589 590
            }
            if (SyncAuthenticPixels(image,exception) == MagickFalse)
              break;
cristy's avatar
cristy committed
591
            status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy's avatar
cristy committed
592
              image->rows);
cristy's avatar
cristy committed
593 594 595 596 597 598 599 600 601 602
            if (status == MagickFalse)
              break;
          }
        break;
      }
      case PseudoClass:
      {
        /*
          Convert X image to PseudoClass packets.
        */
cristy's avatar
cristy committed
603
        if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
cristy's avatar
cristy committed
604
          {
605 606
            if (header.ncolors != 0)
              colors=(XColor *) RelinquishMagickMemory(colors);
cristy's avatar
cristy committed
607 608 609 610
            ximage->data=DestroyString(ximage->data);
            ximage=(XImage *) RelinquishMagickMemory(ximage);
            ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
          }
cristy's avatar
cristy committed
611
        for (i=0; i < (ssize_t) image->colors; i++)
cristy's avatar
cristy committed
612
        {
cristy's avatar
cristy committed
613
          image->colormap[i].red=(MagickRealType) ScaleShortToQuantum(
cristy's avatar
cristy committed
614
            colors[i].red);
cristy's avatar
cristy committed
615
          image->colormap[i].green=(MagickRealType) ScaleShortToQuantum(
cristy's avatar
cristy committed
616
            colors[i].green);
cristy's avatar
cristy committed
617
          image->colormap[i].blue=(MagickRealType) ScaleShortToQuantum(
cristy's avatar
cristy committed
618
            colors[i].blue);
cristy's avatar
cristy committed
619
        }
cristy's avatar
cristy committed
620
        for (y=0; y < (ssize_t) image->rows; y++)
cristy's avatar
cristy committed
621 622
        {
          q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristy's avatar
cristy committed
623
          if (q == (Quantum *) NULL)
cristy's avatar
cristy committed
624
            break;
cristy's avatar
cristy committed
625
          for (x=0; x < (ssize_t) image->columns; x++)
cristy's avatar
cristy committed
626
          {
Cristy's avatar
Cristy committed
627 628
            index=(Quantum) ConstrainColormapIndex(image,(ssize_t)
              XGetPixel(ximage,(int) x,(int) y),exception);
cristy's avatar
cristy committed
629
            SetPixelIndex(image,index,q);
630
            SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
cristy's avatar
cristy committed
631
            q+=GetPixelChannels(image);
cristy's avatar
cristy committed
632 633 634
          }
          if (SyncAuthenticPixels(image,exception) == MagickFalse)
            break;
cristy's avatar
cristy committed
635
          status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristy's avatar
cristy committed
636
            image->rows);
cristy's avatar
cristy committed
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677
          if (status == MagickFalse)
            break;
        }
        break;
      }
    }
  /*
    Free image and colormap.
  */
  if (header.ncolors != 0)
    colors=(XColor *) RelinquishMagickMemory(colors);
  ximage->data=DestroyString(ximage->data);
  ximage=(XImage *) RelinquishMagickMemory(ximage);
  if (EOFBlob(image) != MagickFalse)
    ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
      image->filename);
  (void) CloseBlob(image);
  return(GetFirstImageInList(image));
}
#endif

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   R e g i s t e r X W D I m a g e                                           %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  RegisterXWDImage() adds properties for the XWD image format to
%  the list of supported formats.  The properties include the image format
%  tag, a method to read and/or write the format, whether the format
%  supports the saving of more than one frame to the same file or blob,
%  whether the format supports native in-memory I/O, and a brief
%  description of the format.
%
%  The format of the RegisterXWDImage method is:
%
cristy's avatar
cristy committed
678
%      size_t RegisterXWDImage(void)
cristy's avatar
cristy committed
679 680
%
*/
cristy's avatar
cristy committed
681
ModuleExport size_t RegisterXWDImage(void)
cristy's avatar
cristy committed
682 683 684 685
{
  MagickInfo
    *entry;

dirk's avatar
dirk committed
686
  entry=AcquireMagickInfo("XWD","XWD","X Windows system window dump (color)");
cristy's avatar
cristy committed
687 688 689 690 691
#if defined(MAGICKCORE_X11_DELEGATE)
  entry->decoder=(DecodeImageHandler *) ReadXWDImage;
  entry->encoder=(EncodeImageHandler *) WriteXWDImage;
#endif
  entry->magick=(IsImageFormatHandler *) IsXWD;
dirk's avatar
dirk committed
692
  entry->flags^=CoderAdjoinFlag;
cristy's avatar
cristy committed
693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737
  (void) RegisterMagickInfo(entry);
  return(MagickImageCoderSignature);
}

/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   U n r e g i s t e r X W D I m a g e                                       %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  UnregisterXWDImage() removes format registrations made by the
%  XWD module from the list of supported formats.
%
%  The format of the UnregisterXWDImage method is:
%
%      UnregisterXWDImage(void)
%
*/
ModuleExport void UnregisterXWDImage(void)
{
  (void) UnregisterMagickInfo("XWD");
}

#if defined(MAGICKCORE_X11_DELEGATE)
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                             %
%                                                                             %
%                                                                             %
%   W r i t e X W D I m a g e                                                 %
%                                                                             %
%                                                                             %
%                                                                             %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%  WriteXWDImage() writes an image to a file in X window dump
%  rasterfile format.
%
%  The format of the WriteXWDImage method is:
%
cristy's avatar
cristy committed
738 739
%      MagickBooleanType WriteXWDImage(const ImageInfo *image_info,
%        Image *image,ExceptionInfo *exception)
cristy's avatar
cristy committed
740 741 742 743 744 745 746
%
%  A description of each parameter follows.
%
%    o image_info: the image info.
%
%    o image:  The image.
%
cristy's avatar
cristy committed
747 748
%    o exception: return any errors or warnings in this structure.
%
cristy's avatar
cristy committed
749
*/
cristy's avatar
cristy committed
750 751
static MagickBooleanType WriteXWDImage(const ImageInfo *image_info,Image *image,
  ExceptionInfo *exception)
cristy's avatar
cristy committed
752 753 754 755 756 757 758
{
  const char
    *value;

  MagickBooleanType
    status;

cristy's avatar
cristy committed
759
  register const Quantum
cristy's avatar
cristy committed
760 761
    *p;

cristy's avatar
cristy committed
762
  register ssize_t
cristy's avatar
cristy committed
763 764 765 766 767
    x;

  register unsigned char
    *q;

cristy's avatar
cristy committed
768
  size_t
cristy's avatar
cristy committed
769 770
    bits_per_pixel,
    bytes_per_line,
cristy's avatar
cristy committed
771
    length,
cristy's avatar
cristy committed
772 773
    scanline_pad;

cristy's avatar
cristy committed
774
  ssize_t
Cristy's avatar
Cristy committed
775
    count,
cristy's avatar
cristy committed
776 777 778 779 780
    y;

  unsigned char
    *pixels;

781 782 783
  unsigned long
    lsb_first;

cristy's avatar
cristy committed
784 785 786 787 788 789 790
  XWDFileHeader
    xwd_info;

  /*
    Open output image file.
  */
  assert(image_info != (const ImageInfo *) NULL);
cristy's avatar
cristy committed
791
  assert(image_info->signature == MagickCoreSignature);
cristy's avatar
cristy committed
792
  assert(image != (Image *) NULL);
cristy's avatar
cristy committed
793
  assert(image->signature == MagickCoreSignature);
cristy's avatar
cristy committed
794 795
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy's avatar
cristy committed
796
  assert(exception != (ExceptionInfo *) NULL);
cristy's avatar
cristy committed
797
  assert(exception->signature == MagickCoreSignature);
cristy's avatar
cristy committed
798
  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy's avatar
cristy committed
799 800
  if (status == MagickFalse)
    return(status);
Cristy's avatar
Cristy committed
801 802 803 804 805
  if ((image->columns != (CARD32) image->columns) ||
      (image->rows != (CARD32) image->rows))
    ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
  if ((image->storage_class == PseudoClass) && (image->colors > 256))
    (void) SetImageType(image,TrueColorType,exception);
cristy's avatar
cristy committed
806
  (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy's avatar
cristy committed
807 808 809
  /*
    Initialize XWD file header.
  */
810
  (void) memset(&xwd_info,0,sizeof(xwd_info));
cristy's avatar
cristy committed
811
  xwd_info.header_size=(CARD32) sz_XWDheader;
cristy's avatar
cristy committed
812
  value=GetImageProperty(image,"comment",exception);
cristy's avatar
cristy committed
813 814 815 816 817 818 819 820 821 822 823 824 825
  if (value != (const char *) NULL)
    xwd_info.header_size+=(CARD32) strlen(value);
  xwd_info.header_size++;
  xwd_info.file_version=(CARD32) XWD_FILE_VERSION;
  xwd_info.pixmap_format=(CARD32) ZPixmap;
  xwd_info.pixmap_depth=(CARD32) (image->storage_class == DirectClass ? 24 : 8);
  xwd_info.pixmap_width=(CARD32) image->columns;
  xwd_info.pixmap_height=(CARD32) image->rows;
  xwd_info.xoffset=(CARD32) 0;
  xwd_info.byte_order=(CARD32) MSBFirst;
  xwd_info.bitmap_unit=(CARD32) (image->storage_class == DirectClass ? 32 : 8);
  xwd_info.bitmap_bit_order=(CARD32) MSBFirst;
  xwd_info.bitmap_pad=(CARD32) (image->storage_class == DirectClass ? 32 : 8);
cristy's avatar
cristy committed
826
  bits_per_pixel=(size_t) (image->storage_class == DirectClass ? 24 : 8);
cristy's avatar
cristy committed
827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
  xwd_info.bits_per_pixel=(CARD32) bits_per_pixel;
  bytes_per_line=(CARD32) ((((xwd_info.bits_per_pixel*
    xwd_info.pixmap_width)+((xwd_info.bitmap_pad)-1))/
    (xwd_info.bitmap_pad))*((xwd_info.bitmap_pad) >> 3));
  xwd_info.bytes_per_line=(CARD32) bytes_per_line;
  xwd_info.visual_class=(CARD32)
    (image->storage_class == DirectClass ? DirectColor : PseudoColor);
  xwd_info.red_mask=(CARD32)
    (image->storage_class == DirectClass ? 0xff0000 : 0);
  xwd_info.green_mask=(CARD32)
    (image->storage_class == DirectClass ? 0xff00 : 0);
  xwd_info.blue_mask=(CARD32) (image->storage_class == DirectClass ? 0xff : 0);
  xwd_info.bits_per_rgb=(CARD32) (image->storage_class == DirectClass ? 24 : 8);
  xwd_info.colormap_entries=(CARD32)
    (image->storage_class == DirectClass ? 256 : image->colors);
  xwd_info.ncolors=(unsigned int)
    (image->storage_class == DirectClass ? 0 : image->colors);
  xwd_info.window_width=(CARD32) image->columns;
  xwd_info.window_height=(CARD32) image->rows;
  xwd_info.window_x=0;
  xwd_info.window_y=0;
  xwd_info.window_bdrwidth=(CARD32) 0;
  /*
    Write XWD header.
  */
  lsb_first=1;
  if ((int) (*(char *) &lsb_first) != 0)
    MSBOrderLong((unsigned char *) &xwd_info,sizeof(xwd_info));
  (void) WriteBlob(image,sz_XWDheader,(unsigned char *) &xwd_info);
  if (value != (const char *) NULL)
    (void) WriteBlob(image,strlen(value),(unsigned char *) value);
  (void) WriteBlob(image,1,(const unsigned char *) "\0");
  if (image->storage_class == PseudoClass)
    {
cristy's avatar
cristy committed
861
      register ssize_t
cristy's avatar
cristy committed
862 863
        i;

cristy's avatar
cristy committed
864 865 866 867 868 869 870 871 872
      XColor
        *colors;

      XWDColor
        color;

      /*
        Dump colormap to file.
      */
873
      (void) memset(&color,0,sizeof(color));
cristy's avatar
cristy committed
874 875 876 877
      colors=(XColor *) AcquireQuantumMemory((size_t) image->colors,
        sizeof(*colors));
      if (colors == (XColor *) NULL)
        ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy's avatar
cristy committed
878
      for (i=0; i < (ssize_t) image->colors; i++)
cristy's avatar
cristy committed
879
      {
cristy's avatar
cristy committed
880 881 882 883 884 885 886
        colors[i].pixel=(unsigned long) i;
        colors[i].red=ScaleQuantumToShort(ClampToQuantum(
          image->colormap[i].red));
        colors[i].green=ScaleQuantumToShort(ClampToQuantum(
          image->colormap[i].green));
        colors[i].blue=ScaleQuantumToShort(ClampToQuantum(
          image->colormap[i].blue));
cristy's avatar
cristy committed
887 888 889 890 891 892
        colors[i].flags=(char) (DoRed | DoGreen | DoBlue);
        colors[i].pad='\0';
        if ((int) (*(char *) &lsb_first) != 0)
          {
            MSBOrderLong((unsigned char *) &colors[i].pixel,
              sizeof(colors[i].pixel));
Cristy's avatar
Cristy committed
893 894
            MSBOrderShort((unsigned char *) &colors[i].red,3*
              sizeof(colors[i].red));
cristy's avatar
cristy committed
895 896
          }
      }
cristy's avatar
cristy committed
897
      for (i=0; i < (ssize_t) image->colors; i++)
cristy's avatar
cristy committed
898 899 900 901 902 903
      {
        color.pixel=(CARD32) colors[i].pixel;
        color.red=colors[i].red;
        color.green=colors[i].green;
        color.blue=colors[i].blue;
        color.flags=(CARD8) colors[i].flags;
Cristy's avatar
Cristy committed
904 905 906
        count=WriteBlob(image,sz_XWDColor,(unsigned char *) &color);
        if (count != (ssize_t) sz_XWDColor)
          break;
cristy's avatar
cristy committed
907 908 909 910 911 912 913 914 915 916 917 918
      }
      colors=(XColor *) RelinquishMagickMemory(colors);
    }
  /*
    Allocate memory for pixels.
  */
  length=3*bytes_per_line;
  if (image->storage_class == PseudoClass)
    length=bytes_per_line;
  pixels=(unsigned char *) AcquireQuantumMemory(length,sizeof(*pixels));
  if (pixels == (unsigned char *) NULL)
    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
919
  (void) memset(pixels,0,length);
cristy's avatar
cristy committed
920 921 922 923
  /*
    Convert MIFF to XWD raster pixels.
  */
  scanline_pad=(bytes_per_line-((image->columns*bits_per_pixel) >> 3));
cristy's avatar
cristy committed
924
  for (y=0; y < (ssize_t) image->rows; y++)
cristy's avatar
cristy committed
925
  {
cristy's avatar
cristy committed
926
    p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy's avatar
cristy committed
927
    if (p == (const Quantum *) NULL)
cristy's avatar
cristy committed
928 929 930 931
      break;
    q=pixels;
    if (image->storage_class == PseudoClass)
      {
cristy's avatar
cristy committed
932
        for (x=0; x < (ssize_t) image->columns; x++)
cristy's avatar
cristy committed
933 934
        {
          *q++=(unsigned char) GetPixelIndex(image,p);
cristy's avatar
cristy committed
935
          p+=GetPixelChannels(image);
cristy's avatar
cristy committed
936
        }
cristy's avatar
cristy committed
937 938
      }
    else
cristy's avatar
cristy committed
939
      for (x=0; x < (ssize_t) image->columns; x++)
cristy's avatar
cristy committed
940
      {
cristy's avatar
cristy committed
941 942 943
        *q++=ScaleQuantumToChar(GetPixelRed(image,p));
        *q++=ScaleQuantumToChar(GetPixelGreen(image,p));
        *q++=ScaleQuantumToChar(GetPixelBlue(image,p));
cristy's avatar
cristy committed
944
        p+=GetPixelChannels(image);
cristy's avatar
cristy committed
945
      }
cristy's avatar
cristy committed
946
    for (x=0; x < (ssize_t) scanline_pad; x++)
cristy's avatar
cristy committed
947
      *q++='\0';
Cristy's avatar
Cristy committed
948 949 950 951
    length=(size_t) (q-pixels);
    count=WriteBlob(image,length,pixels);
    if (count != (ssize_t) length)
      break;
cristy's avatar
cristy committed
952
    status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
cristy's avatar
cristy committed
953
      image->rows);
cristy's avatar
cristy committed
954 955 956 957 958
    if (status == MagickFalse)
      break;
  }
  pixels=(unsigned char *) RelinquishMagickMemory(pixels);
  (void) CloseBlob(image);
Cristy's avatar
Cristy committed
959
  return(y < (ssize_t) image->rows ? MagickFalse :  MagickTrue);
cristy's avatar
cristy committed
960 961
}
#endif