1/** <title>NSBitmapImageRep.m</title>
2
3   <abstract>Bitmap image representation.</abstract>
4
5   Copyright (C) 1996-2017 Free Software Foundation, Inc.
6
7   Author:  Adam Fedor <fedor@gnu.org>
8   Date: Feb 1996
9
10   This file is part of the GNUstep GUI Library.
11
12   This library is free software; you can redistribute it and/or
13   modify it under the terms of the GNU Lesser General Public
14   License as published by the Free Software Foundation; either
15   version 2 of the License, or (at your option) any later version.
16
17   This library is distributed in the hope that it will be useful,
18   but WITHOUT ANY WARRANTY; without even the implied warranty of
19   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
20   Lesser General Public License for more details.
21
22   You should have received a copy of the GNU Lesser General Public
23   License along with this library; see the file COPYING.LIB.
24   If not, see <http://www.gnu.org/licenses/> or write to the
25   Free Software Foundation, 51 Franklin Street, Fifth Floor,
26   Boston, MA 02110-1301, USA.
27*/
28
29#import "config.h"
30
31#include <stdlib.h>
32#include <math.h>
33#include <tiff.h>
34
35#import <Foundation/NSArray.h>
36#import <Foundation/NSAutoreleasePool.h>
37#import <Foundation/NSData.h>
38#import <Foundation/NSDebug.h>
39#import <Foundation/NSException.h>
40#import <Foundation/NSFileManager.h>
41#import <Foundation/NSValue.h>
42#import "AppKit/AppKitExceptions.h"
43#import "AppKit/NSGraphics.h"
44#import "AppKit/NSGraphicsContext.h"
45#import "AppKit/NSPasteboard.h"
46#import "AppKit/NSView.h"
47#import "AppKit/NSBitmapImageRep.h"
48
49#import "NSBitmapImageRep+GIF.h"
50#import "NSBitmapImageRep+JPEG.h"
51#import "NSBitmapImageRep+PNG.h"
52#import "NSBitmapImageRep+PNM.h"
53#import "NSBitmapImageRep+ICNS.h"
54#import "NSBitmapImageRepPrivate.h"
55#import "GSGuiPrivate.h"
56
57#include "nsimage-tiff.h"
58
59/* Maximum number of planes */
60#define MAX_PLANES 5
61
62
63/**
64  <unit>
65  <heading>Class Description</heading>
66  <p>
67  NSBitmapImageRep is an image representation for handling images composed
68  of pixels. The standard image format for NSBitmapImageRep is the TIFF
69  format. However, through the use of image filters and other methods, many
70  other standard image formats can be handled by NSBitmapImageRep.
71
72  Images are typically handled through the NSImage class and there is often
73  no need to use the NSBitmapImageRep class directly. However there may
74  be cases where you want to manipulate the image bitmap data directly.
75  </p>
76  </unit>
77*/
78@implementation NSBitmapImageRep
79
80/** Returns YES if the image stored in data can be read and decoded */
81+ (BOOL) canInitWithData: (NSData *)data
82{
83  if (data == nil)
84    {
85      return NO;
86    }
87
88#if HAVE_LIBPNG
89  if ([self _bitmapIsPNG: data])
90    return YES;
91#endif
92
93  if ([self _bitmapIsPNM: data])
94    return YES;
95
96#if HAVE_LIBJPEG
97  if ([self _bitmapIsJPEG: data])
98    return YES;
99#endif
100
101#if HAVE_LIBUNGIF || HAVE_LIBGIF
102  if ([self _bitmapIsGIF: data])
103    return YES;
104#endif
105
106  if ([self _bitmapIsICNS: data])
107    return YES;
108
109  if ([self _bitmapIsTIFF: data])
110    return YES;
111
112  return NO;
113}
114
115/** Returns a list of image filename extensions that are understood by
116    NSBitmapImageRep.  */
117+ (NSArray *) imageUnfilteredFileTypes
118{
119  static NSArray *types = nil;
120
121  if (types == nil)
122    {
123      types = [[NSArray alloc] initWithObjects:
124	@"tiff", @"tif",
125	@"pnm", @"ppm",
126#if HAVE_LIBUNGIF || HAVE_LIBGIF
127	@"gif",
128#endif
129#if HAVE_LIBJPEG
130	@"jpeg", @"jpg",
131#endif
132#if HAVE_LIBPNG
133	@"png",
134#endif
135	@"icns",
136	nil];
137    }
138
139  return types;
140}
141
142/** Returns a list of image pasteboard types that are understood by
143    NSBitmapImageRep.  */
144+ (NSArray *) imageUnfilteredPasteboardTypes
145{
146  static NSArray *types = nil;
147
148  if (types == nil)
149    {
150      types = [[NSArray alloc] initWithObjects: NSTIFFPboardType, nil];
151    }
152
153  return types;
154}
155
156/** <p>Returns a newly allocated NSBitmapImageRep object representing the
157    image stored in imageData. If the image data contains more than one
158    image, the first one is choosen.</p><p>See Also: +imageRepsWithData:</p>
159*/
160+ (id) imageRepWithData: (NSData *)imageData
161{
162  return AUTORELEASE([[self alloc] initWithData: imageData]);
163}
164
165/**<p>Returns an array containing newly allocated NSBitmapImageRep
166    objects representing the images stored in imageData.</p>
167    <p>See Also: +imageRepWithData:</p>
168*/
169+ (NSArray*) imageRepsWithData: (NSData *)imageData
170{
171  if (imageData == nil)
172    {
173      NSLog(@"NSBitmapImageRep: nil image data");
174      return [NSArray array];
175    }
176
177  if ([self _bitmapIsPNG: imageData])
178    {
179      NSBitmapImageRep *rep;
180      NSArray *a;
181
182      rep = [[self alloc] _initBitmapFromPNG: imageData];
183      if (!rep)
184        return [NSArray array];
185      a = [NSArray arrayWithObject: rep];
186      DESTROY(rep);
187      return a;
188    }
189
190  if ([self _bitmapIsPNM: imageData])
191    {
192      NSBitmapImageRep *rep;
193      NSArray *a;
194
195      rep = [[self alloc] _initBitmapFromPNM: imageData
196			      errorMessage: NULL];
197      if (!rep)
198        return [NSArray array];
199      a = [NSArray arrayWithObject: rep];
200      DESTROY(rep);
201      return a;
202    }
203
204  if ([self _bitmapIsJPEG: imageData])
205    {
206      NSBitmapImageRep *rep;
207      NSArray *a;
208
209      rep = [[self alloc] _initBitmapFromJPEG: imageData
210			       errorMessage: NULL];
211      if (!rep)
212        return [NSArray array];
213      a = [NSArray arrayWithObject: rep];
214      DESTROY(rep);
215      return a;
216    }
217
218  if ([self _bitmapIsGIF: imageData])
219    {
220      NSBitmapImageRep *rep;
221      NSArray *a;
222
223      rep = [[self alloc] _initBitmapFromGIF: imageData
224			      errorMessage: NULL];
225      if (!rep)
226        return [NSArray array];
227      a = [NSArray arrayWithObject: rep];
228      DESTROY(rep);
229      return a;
230    }
231
232  if ([self _bitmapIsICNS: imageData])
233    {
234      return [self _imageRepsWithICNSData: imageData];
235    }
236
237  if ([self _bitmapIsTIFF: imageData])
238    {
239      return [self _imageRepsWithTIFFData: imageData];
240    }
241
242  NSLog(@"NSBitmapImageRep: unable to parse bitmap image data");
243  return [NSArray array];
244}
245
246/** Loads only the default (first) image from the image contained in
247   data. */
248- (id) initWithData: (NSData *)imageData
249{
250  Class class;
251
252  if (imageData == nil)
253    {
254      RELEASE(self);
255      return nil;
256    }
257
258  class = [self class];
259  if ([class _bitmapIsPNG: imageData])
260    return [self _initBitmapFromPNG: imageData];
261
262  if ([class _bitmapIsPNM: imageData])
263    return [self _initBitmapFromPNM: imageData
264		       errorMessage: NULL];
265
266  if ([class _bitmapIsJPEG: imageData])
267    return [self _initBitmapFromJPEG: imageData
268			errorMessage: NULL];
269
270  if ([class _bitmapIsGIF: imageData])
271    return [self _initBitmapFromGIF: imageData
272		       errorMessage: NULL];
273
274  if ([class _bitmapIsICNS: imageData])
275    return [self _initBitmapFromICNS: imageData];
276
277  if ([class _bitmapIsTIFF: imageData])
278    return [self _initBitmapFromTIFF: imageData];
279
280  DESTROY(self);
281  return nil;
282}
283
284/** Initialize with bitmap data from a rect within the focused view */
285- (id) initWithFocusedViewRect: (NSRect)rect
286{
287  NSInteger bps, spp, alpha, format;
288  NSSize size;
289  NSString *space;
290  unsigned char *planes[4];
291  NSDictionary *dict;
292
293  dict = [GSCurrentContext() GSReadRect: rect];
294  if (dict == nil)
295    {
296      NSLog(@"NSBitmapImageRep initWithFocusedViewRect: failed");
297      RELEASE(self);
298      return nil;
299    }
300  _imageData = RETAIN([dict objectForKey: @"Data"]);
301  if (_imageData == nil || [_imageData length] == 0)
302    {
303      NSLog(@"NSBitmapImageRep initWithFocusedViewRect: failed");
304      RELEASE(self);
305      return nil;
306    }
307  bps = [[dict objectForKey: @"BitsPerSample"] intValue];
308  if (bps == 0)
309    bps = 8;
310  spp = [[dict objectForKey: @"SamplesPerPixel"] intValue];
311  alpha = [[dict objectForKey: @"HasAlpha"] intValue];
312  size = [[dict objectForKey: @"Size"] sizeValue];
313  space = [dict objectForKey: @"ColorSpace"];
314  format = [[dict objectForKey: @"BitmapFormat"] intValue];
315  planes[0] = (unsigned char *)[_imageData bytes];
316  self = [self initWithBitmapDataPlanes: planes
317               pixelsWide: size.width
318               pixelsHigh: size.height
319               bitsPerSample: bps
320               samplesPerPixel: spp
321               hasAlpha: (alpha) ? YES : NO
322               isPlanar: NO
323               colorSpaceName: space
324               bitmapFormat: format
325               bytesPerRow: 0
326               bitsPerPixel: 0];
327  return self;
328}
329
330/**
331    <init />
332    <p>
333    Initializes a newly created NSBitmapImageRep object to hold image data
334    specified in the planes buffer and organized according to the
335    additional arguments passed into the method.
336    </p>
337    <p>
338    The planes argument is an array of char pointers where each array
339    holds a single component or plane of data. Note that if data is
340    passed into the method via planes, the data is NOT copied and not
341    freed when the object is deallocated. It is assumed that the data
342    will always be available. If planes is NULL, then a suitable amount
343    of memory will be allocated to store the information needed. One can
344    then obtain a pointer to the planes data using the -bitmapData or
345    -getBitmapDataPlanes: method.
346    </p>
347    <p>
348    Each component of the data is in "standard" order, such as red, green,
349    blue for RGB color images. The transparency component, if these is one, should
350    always be last.
351    </p>
352    <p>
353    The other arguments to the method consist of:
354    </p>
355    <deflist>
356      <term>width and height</term>
357      <desc>The width and height of the image in pixels</desc>
358      <term>bps</term>
359      <desc>
360      The bits per sample or the number of bits used to store a number in
361      one component of one pixel of the image. Typically this is 8 (bits)
362      but can be 2 or 4, although not all values are supported.
363      </desc>
364      <term>spp</term>
365      <desc>
366      Samples per pixel, or the number of components of color in the pixel.
367      For instance this would be 4 for an RGB image with transparency.
368      </desc>
369      <term>alpha</term>
370      <desc>
371      Set to YES if the image has a transparency component.
372      </desc>
373      <term>isPlanar</term>
374      <desc>
375      Set to YES if the data is arranged in planes, i.e. one component
376      per buffer as stored in the planes array. If NO, then the image data
377      is mixed in one buffer. For instance, for RGB data, the first sample
378      would contain red, then next green, then blue, followed by red for the
379      next pixel.
380      </desc>
381      <term>colorSpaceName</term>
382      <desc>
383      This argument specifies how the data values are to be interpreted.
384      Possible values include the typical colorspace names (although
385      not all values are currently supported)
386      </desc>
387      <term>rowBytes</term>
388      <desc>
389      Specifies the number of bytes contained in a single scan line of the
390      data. Normally this can be computed from the width of the image,
391      the samples per pixel and the bits per sample. However, if the data
392      is aligned along word boundaries, this value may differ from this.
393      If rowBytes is 0, the method will calculate the value assuming there
394      are no extra bytes at the end of the scan line.
395      </desc>
396      <term>pixelBits</term>
397      <desc>
398      This is normally bps for planar data and bps times spp for non-planar
399      data, but sometimes images have extra bits. If pixelBits is 0 it
400      will be calculated as described above.
401      </desc>
402      </deflist>
403*/
404- (id) initWithBitmapDataPlanes: (unsigned char **)planes
405                     pixelsWide: (NSInteger)width
406                     pixelsHigh: (NSInteger)height
407                  bitsPerSample: (NSInteger)bitsPerSample
408                samplesPerPixel: (NSInteger)samplesPerPixel
409                       hasAlpha: (BOOL)alpha
410                       isPlanar: (BOOL)isPlanar
411                 colorSpaceName: (NSString *)colorSpaceName
412                    bytesPerRow: (NSInteger)rowBytes
413                   bitsPerPixel: (NSInteger)pixelBits
414{
415  return [self initWithBitmapDataPlanes: planes
416               pixelsWide: width
417               pixelsHigh: height
418               bitsPerSample: bitsPerSample
419               samplesPerPixel: samplesPerPixel
420               hasAlpha: alpha
421               isPlanar: isPlanar
422               colorSpaceName: colorSpaceName
423               bitmapFormat: 0
424               bytesPerRow: rowBytes
425               bitsPerPixel: pixelBits];
426}
427
428- (id) initWithBitmapDataPlanes: (unsigned char**)planes
429                     pixelsWide: (NSInteger)width
430                     pixelsHigh: (NSInteger)height
431                  bitsPerSample: (NSInteger)bps
432                samplesPerPixel: (NSInteger)spp
433                       hasAlpha: (BOOL)alpha
434                       isPlanar: (BOOL)isPlanar
435                 colorSpaceName: (NSString*)colorSpaceName
436                   bitmapFormat: (NSBitmapFormat)bitmapFormat
437                    bytesPerRow: (NSInteger)rowBytes
438                   bitsPerPixel: (NSInteger)pixelBits
439{
440  NSDebugLLog(@"NSImage", @"Creating bitmap image with pw %d ph %d bps %d spp %d alpha %d, planar %d cs %@",
441              (int)width,(int) height, (int)bps, (int)spp, alpha, isPlanar, colorSpaceName);
442  if (!bps || !spp || !width || !height)
443    {
444      [NSException raise: NSInvalidArgumentException
445        format: @"Required arguments not specified creating NSBitmapImageRep"];
446    }
447
448  _pixelsWide = width;
449  _pixelsHigh = height;
450  _size.width  = width;
451  _size.height = height;
452  _bitsPerSample = bps;
453  _numColors  = spp;
454  _hasAlpha   = alpha;
455  _isPlanar   = isPlanar;
456  _colorSpace = RETAIN(colorSpaceName);
457  _format = bitmapFormat;
458  if (!pixelBits)
459    pixelBits = bps * ((_isPlanar) ? 1 : spp);
460  _bitsPerPixel = pixelBits;
461  if (!rowBytes)
462    rowBytes = ceil((float)width * _bitsPerPixel / 8);
463  _bytesPerRow = rowBytes;
464
465  _imagePlanes = NSAllocateCollectable(sizeof(unsigned char*) * MAX_PLANES, 0);
466  if (planes)
467    {
468      unsigned int i;
469
470      for (i = 0; i < MAX_PLANES; i++)
471 	_imagePlanes[i] = NULL;
472      for (i = 0; i < ((_isPlanar) ? _numColors : 1); i++)
473 	_imagePlanes[i] = planes[i];
474    }
475  else
476    {
477      unsigned char *bits;
478      NSUInteger length;
479      unsigned int i;
480
481      // No image data was given, allocate it.
482      length = (NSUInteger)((_isPlanar) ? _numColors : 1) * _bytesPerRow *
483	  _pixelsHigh * sizeof(unsigned char);
484      // Create a mutable data object although we never use it as such
485      _imageData = [[NSMutableData alloc] initWithLength: length];
486      bits = (unsigned char *)[_imageData bytes];
487      _imagePlanes[0] = bits;
488      if (_isPlanar)
489	{
490	  for (i = 1; i < _numColors; i++)
491	    _imagePlanes[i] = bits + i * _bytesPerRow * _pixelsHigh;
492	  for (i = _numColors; i < MAX_PLANES; i++)
493	    _imagePlanes[i] = NULL;
494	}
495      else
496	{
497	  for (i = 1; i < MAX_PLANES; i++)
498	    _imagePlanes[i] = NULL;
499	}
500    }
501
502  if (alpha)
503    {
504      unsigned char	*bData = (unsigned char*)[self bitmapData];
505      BOOL		allOpaque = YES;
506      unsigned		offset = _numColors - 1;
507      unsigned		limit = _size.height * _size.width;
508      unsigned		i;
509
510      for (i = 0; i < limit; i++)
511	{
512	  unsigned	a;
513
514	  bData += offset;
515	  a = *bData++;
516	  if (a != 255)
517	    {
518	      allOpaque = NO;
519	      break;
520	    }
521	}
522      [self setOpaque: allOpaque];
523    }
524  else
525    {
526      [self setOpaque: YES];
527    }
528  _properties = [[NSMutableDictionary alloc] init];
529
530  return self;
531}
532
533- (void)colorizeByMappingGray:(CGFloat)midPoint
534		      toColor:(NSColor *)midPointColor
535		 blackMapping:(NSColor *)shadowColor
536		 whiteMapping:(NSColor *)lightColor
537{
538  // TODO
539}
540
541- (id)initWithBitmapHandle:(void *)bitmap
542{
543  // TODO Only needed on MS Windows
544  RELEASE(self);
545  return nil;
546}
547
548- (id)initWithIconHandle:(void *)icon
549{
550  // TODO Only needed on MS Windows
551  RELEASE(self);
552  return nil;
553}
554
555- (id) initForIncrementalLoad
556{
557  // FIXME
558  return self;
559}
560
561- (NSInteger) incrementalLoadFromData: (NSData *)data complete: (BOOL)complete
562{
563  if (!complete)
564    {
565      // we don't implement it really
566      return NSImageRepLoadStatusWillNeedAllData;
567    }
568  return [self initWithData: data] ? NSImageRepLoadStatusCompleted : NSImageRepLoadStatusUnexpectedEOF;
569}
570
571- (void) dealloc
572{
573  NSZoneFree([self zone],_imagePlanes);
574  RELEASE(_imageData);
575  RELEASE(_properties);
576  [super dealloc];
577}
578
579//
580// Getting Information about the Image
581//
582/** Returns the number of bits need to contain one pixels worth of data.
583    This is normally the number of samples per pixel times the number of
584    bits in one sample. */
585- (NSInteger) bitsPerPixel
586{
587  return _bitsPerPixel;
588}
589
590/** Returns the number of samples in a pixel. For instance, a normal RGB
591    image with transparency would have a samplesPerPixel of 4.  */
592- (NSInteger) samplesPerPixel
593{
594  return _numColors;
595}
596
597/** Returns YES if the image components are stored separately. Returns
598    NO if the components are meshed (i.e. all the samples for one pixel
599    come before the next pixel).  */
600- (BOOL) isPlanar
601{
602  return _isPlanar;
603}
604
605/** Returns the number of planes in an image.  Typically this is
606    equal to the number of samples in a planar image or 1 for a non-planar
607    image.  */
608- (NSInteger) numberOfPlanes
609{
610  return (_isPlanar) ? _numColors : 1;
611}
612
613/** Returns the number of bytes in a plane. This is the number of bytes
614    in a row times tne height of the image.  */
615- (NSInteger) bytesPerPlane
616{
617  return _bytesPerRow*_pixelsHigh;
618}
619
620/** Returns the number of bytes in a row. This is typically based on the
621    width of the image and the bits per sample and samples per pixel (if
622    in medhed configuration). However it may differ from this if set
623    explicitly in -initWithBitmapDataPlanes:pixelsWide:pixelsHigh:bitsPerSample:samplesPerPixel:hasAlpha:isPlanar:colorSpaceName:bytesPerRow:bitsPerPixel:.
624*/
625- (NSInteger) bytesPerRow
626{
627  return _bytesPerRow;
628}
629
630//
631// Getting Image Data
632//
633/** Returns the first plane of data representing the image.  */
634- (unsigned char *) bitmapData
635{
636  unsigned char *planes[MAX_PLANES];
637  [self getBitmapDataPlanes: planes];
638  return planes[0];
639}
640
641/** Files the array data with pointers to each of the data planes
642    representing the image. The data array must be allocated to contain
643    at least -samplesPerPixel pointers.  */
644- (void) getBitmapDataPlanes: (unsigned char **)data
645{
646  unsigned int i;
647
648  if (data)
649    {
650      for (i = 0; i < _numColors; i++)
651        {
652          data[i] = _imagePlanes[i];
653        }
654    }
655}
656
657- (NSBitmapFormat) bitmapFormat
658{
659  return _format;
660}
661
662/*
663 * This code was copied over from XGBitmap.m
664 * Here we extract a value a given number of bits wide from a bit
665 * offset into a block of memory starting at "base". The bit numbering
666 * is assumed to be such that a bit offset of zero and a width of 4 gives
667 * the upper 4 bits of the first byte, *not* the lower 4 bits. We do allow
668 * the value to cross a byte boundary, though it is unclear as to whether
669 * this is strictly necessary for OpenStep tiffs.
670 */
671static unsigned int
672_get_bit_value(unsigned char *base, long msb_off, int bit_width)
673{
674  long lsb_off, byte1, byte2;
675  int shift, value;
676
677  /*
678   * Firstly we calculate the position of the msb and lsb in terms
679   * of bit offsets and thus byte offsets. The shift is the number of
680   * spare bits left in the byte containing the lsb
681   */
682  lsb_off= msb_off+bit_width-1;
683  byte1= msb_off/8;
684  byte2= lsb_off/8;
685  shift= 7-(lsb_off%8);
686
687  /*
688   * We now get the value from the byte array, possibly using two bytes if
689   * the required set of bits crosses the byte boundary. This is then shifted
690   * down to it's correct position and extraneous bits masked off before
691   * being returned.
692   */
693  value=base[byte2];
694  if (byte1!=byte2)
695    value|= base[byte1]<<8;
696  value >>= shift;
697
698  return value & ((1<<bit_width)-1);
699}
700
701/**
702 * Returns the values of the components of pixel (x,y), where (0,0) is the
703 * top-left pixel in the image, by storing them in the array pixelData.
704 */
705- (void) getPixel: (NSUInteger[])pixelData atX: (NSInteger)x y: (NSInteger)y
706{
707  NSInteger i;
708  NSInteger offset;
709  NSInteger line_offset;
710
711  if (x < 0 || y < 0 || x >= _pixelsWide || y >= _pixelsHigh)
712    {
713      // outside
714      return;
715    }
716
717  line_offset = _bytesPerRow * y;
718  if (_isPlanar)
719    {
720      if (_bitsPerSample == 8)
721        {
722          offset = x + line_offset;
723          for (i = 0; i < _numColors; i++)
724            {
725              pixelData[i] = _imagePlanes[i][offset];
726            }
727        }
728      else
729        {
730          offset = _bitsPerPixel * x;
731          for (i = 0; i < _numColors; i++)
732            {
733              pixelData[i] = _get_bit_value(_imagePlanes[i] + line_offset,
734                                            offset, _bitsPerSample);
735            }
736        }
737    }
738  else
739    {
740      if (_bitsPerSample == 8)
741        {
742          offset = (_bitsPerPixel * x) / 8 + line_offset;
743          for (i = 0; i < _numColors; i++)
744            {
745              pixelData[i] = _imagePlanes[0][offset + i];
746            }
747        }
748      else
749        {
750          offset = _bitsPerPixel * x;
751          for (i = 0; i < _numColors; i++)
752            {
753              pixelData[i] = _get_bit_value(_imagePlanes[0] + line_offset,
754                                            offset, _bitsPerSample);
755              offset += _bitsPerSample;
756            }
757        }
758    }
759}
760
761static void
762_set_bit_value(unsigned char *base, long msb_off, int bit_width,
763               unsigned int value)
764{
765  long lsb_off, byte1, byte2;
766  int shift;
767  int all;
768
769  /*
770   * Firstly we calculate the position of the msb and lsb in terms
771   * of bit offsets and thus byte offsets. The shift is the number of
772   * spare bits left in the byte containing the lsb
773   */
774  lsb_off= msb_off+bit_width-1;
775  byte1= msb_off/8;
776  byte2= lsb_off/8;
777  shift= 7-(lsb_off%8);
778
779  /*
780   * We now set the value in the byte array, possibly using two bytes if
781   * the required set of bits crosses the byte boundary. This value is
782   * first shifted up to it's correct position and extraneous bits are
783   * masked off.
784   */
785  value &= ((1<<bit_width)-1);
786  value <<= shift;
787  all = ((1<<bit_width)-1) << shift;
788
789  if (byte1 != byte2)
790    base[byte1] = (value >> 8) | (base[byte1] & ~(all >> 8));
791  base[byte2] = (value & 255) | (base[byte2] & ~(all & 255));
792}
793
794/**
795 * Sets the components of pixel (x,y), where (0,0) is the top-left pixel in
796 * the image, to the given array of pixel components.
797 */
798- (void) setPixel: (NSUInteger[])pixelData atX: (NSInteger)x y: (NSInteger)y
799{
800  NSInteger i;
801  NSInteger offset;
802  NSInteger line_offset;
803
804  if (x < 0 || y < 0 || x >= _pixelsWide || y >= _pixelsHigh)
805    {
806      // outside
807      return;
808    }
809
810  if (!_imagePlanes || !_imagePlanes[0])
811    {
812      // allocate plane memory
813      [self bitmapData];
814    }
815
816  line_offset = _bytesPerRow * y;
817  if (_isPlanar)
818    {
819      if (_bitsPerSample == 8)
820        {
821          offset = x + line_offset;
822          for (i = 0; i < _numColors; i++)
823            {
824              _imagePlanes[i][offset] = pixelData[i];
825            }
826        }
827      else
828        {
829          offset = _bitsPerPixel * x;
830          for (i = 0; i < _numColors; i++)
831            {
832              _set_bit_value(_imagePlanes[i] + line_offset,
833                             offset, _bitsPerSample, pixelData[i]);
834            }
835        }
836    }
837  else
838    {
839      if (_bitsPerSample == 8)
840        {
841          offset = (_bitsPerPixel * x) / 8 + line_offset;
842          for (i = 0; i < _numColors; i++)
843            {
844              _imagePlanes[0][offset + i] = pixelData[i];
845            }
846        }
847      else
848        {
849          offset = _bitsPerPixel * x;
850          for (i = 0; i < _numColors; i++)
851            {
852              _set_bit_value(_imagePlanes[0] + line_offset,
853                             offset, _bitsPerSample, pixelData[i]);
854              offset += _bitsPerSample;
855            }
856        }
857    }
858}
859
860/**
861 * Returns an NSColor object representing the color of the pixel (x,y), where
862 * (0,0) is the top-left pixel in the image.
863 */
864- (NSColor*) colorAtX: (NSInteger)x y: (NSInteger)y
865{
866  NSUInteger pixelData[5];
867
868  if (x < 0 || y < 0 || x >= _pixelsWide || y >= _pixelsHigh)
869    {
870      // outside
871      return nil;
872    }
873
874  [self getPixel: pixelData atX: x y: y];
875  if ([_colorSpace isEqualToString: NSCalibratedRGBColorSpace]
876      || [_colorSpace isEqualToString: NSDeviceRGBColorSpace])
877    {
878      NSUInteger ir, ig, ib, ia;
879      CGFloat fr, fg, fb, fa;
880      CGFloat scale;
881
882      scale = (CGFloat)((1 << _bitsPerSample) - 1);
883      if (_hasAlpha)
884        {
885          // This order depends on the bitmap format
886          if (_format & NSAlphaFirstBitmapFormat)
887            {
888              ia = pixelData[0];
889              ir = pixelData[1];
890              ig = pixelData[2];
891              ib = pixelData[3];
892            }
893          else
894            {
895              ir = pixelData[0];
896              ig = pixelData[1];
897              ib = pixelData[2];
898              ia = pixelData[3];
899            }
900
901          // Scale to [0.0 ... 1.0] and undo premultiplication
902          fa = ia / scale;
903          if (_format & NSAlphaNonpremultipliedBitmapFormat)
904            {
905              fr = ir / scale;
906              fg = ig / scale;
907              fb = ib / scale;
908            }
909          else
910            {
911              fr = ir / (scale * fa);
912              fg = ig / (scale * fa);
913              fb = ib / (scale * fa);
914            }
915        }
916      else
917        {
918          ir = pixelData[0];
919          ig = pixelData[1];
920          ib = pixelData[2];
921          // Scale to [0.0 ... 1.0]
922          fr = ir / scale;
923          fg = ig / scale;
924          fb = ib / scale;
925          fa = 1.0;
926        }
927      if ([_colorSpace isEqualToString: NSCalibratedRGBColorSpace])
928        {
929          return [NSColor colorWithCalibratedRed: fr
930                          green: fg
931                          blue: fb
932                          alpha: fa];
933        }
934      else
935        {
936          return [NSColor colorWithDeviceRed: fr
937                          green: fg
938                          blue: fb
939                          alpha: fa];
940        }
941    }
942  else if ([_colorSpace isEqual: NSDeviceWhiteColorSpace]
943           || [_colorSpace isEqual: NSCalibratedWhiteColorSpace])
944    {
945      NSUInteger iw, ia;
946      CGFloat fw, fa;
947      CGFloat scale;
948
949      scale = (CGFloat)((1 << _bitsPerSample) - 1);
950      if (_hasAlpha)
951        {
952          // FIXME: This order depends on the bitmap format
953          if (_format & NSAlphaFirstBitmapFormat)
954            {
955                ia = pixelData[0];
956                iw = pixelData[1];
957            }
958          else
959            {
960                iw = pixelData[0];
961                ia = pixelData[1];
962            }
963
964          // Scale to [0.0 ... 1.0] and undo premultiplication
965          fa = ia / scale;
966          if (_format & NSAlphaNonpremultipliedBitmapFormat)
967            {
968              fw = iw / scale;
969            }
970          else
971            {
972              fw = iw / (scale * fa);
973            }
974        }
975      else
976        {
977          // FIXME: This order depends on the bitmap format
978          iw = pixelData[0];
979          // Scale to [0.0 ... 1.0]
980          fw = iw / scale;
981          fa = 1.0;
982        }
983      if ([_colorSpace isEqualToString: NSCalibratedWhiteColorSpace])
984        {
985          return [NSColor colorWithCalibratedWhite: fw
986                          alpha: fa];
987        }
988      else
989        {
990          return [NSColor colorWithDeviceWhite: fw
991                          alpha: fa];
992        }
993    }
994  else if ([_colorSpace isEqual: NSDeviceBlackColorSpace]
995           || [_colorSpace isEqual: NSCalibratedBlackColorSpace])
996    {
997      NSUInteger ib, ia;
998      CGFloat fw, fa;
999      CGFloat scale;
1000
1001      scale = (CGFloat)((1 << _bitsPerSample) - 1);
1002      if (_hasAlpha)
1003        {
1004          // This order depends on the bitmap format
1005          if (_format & NSAlphaFirstBitmapFormat)
1006            {
1007              ia = pixelData[0];
1008              ib = pixelData[1];
1009            }
1010          else
1011            {
1012              ib = pixelData[0];
1013              ia = pixelData[1];
1014            }
1015          // Scale to [0.0 ... 1.0] and undo premultiplication
1016          fa = ia / scale;
1017         if (_format & NSAlphaNonpremultipliedBitmapFormat)
1018           {
1019             fw = 1.0 - ib / scale;
1020           }
1021         else
1022           {
1023             fw = 1.0 - ib / (scale * fa);
1024           }
1025        }
1026      else
1027        {
1028          ib = pixelData[0];
1029          // Scale to [0.0 ... 1.0]
1030          fw = 1.0 - ib / scale;
1031          fa = 1.0;
1032        }
1033      if ([_colorSpace isEqualToString: NSCalibratedBlackColorSpace])
1034        {
1035          return [NSColor colorWithCalibratedWhite: fw
1036                          alpha: fa];
1037        }
1038      else
1039        {
1040          return [NSColor colorWithDeviceWhite: fw
1041                          alpha: fa];
1042        }
1043		}
1044  else if ([_colorSpace isEqual: NSDeviceCMYKColorSpace])
1045    {
1046      NSUInteger ic, im, iy, ib, ia;
1047      CGFloat fc, fm, fy, fb, fa;
1048      CGFloat scale;
1049
1050      scale = (CGFloat)((1 << _bitsPerSample) - 1);
1051      if (_hasAlpha)
1052        {
1053          // This order depends on the bitmap format
1054          if (_format & NSAlphaFirstBitmapFormat)
1055            {
1056              ia = pixelData[0];
1057              ic = pixelData[1];
1058              im = pixelData[2];
1059              iy = pixelData[3];
1060              ib = pixelData[4];
1061            }
1062          else
1063            {
1064              ic = pixelData[0];
1065              im = pixelData[1];
1066              iy = pixelData[2];
1067              ib = pixelData[3];
1068              ia = pixelData[4];
1069            }
1070
1071          // Scale to [0.0 ... 1.0] and undo premultiplication
1072          fa = ia / scale;
1073          if (_format & NSAlphaNonpremultipliedBitmapFormat)
1074            {
1075              fc = ic / scale;
1076              fm = im / scale;
1077              fy = iy / scale;
1078              fb = ib / scale;
1079            }
1080          else
1081            {
1082              fc = ic / (scale * fa);
1083              fm = im / (scale * fa);
1084              fy = iy / (scale * fa);
1085              fb = ib / (scale * fa);
1086            }
1087        }
1088      else
1089        {
1090          ic = pixelData[0];
1091          im = pixelData[1];
1092          iy = pixelData[2];
1093          ib = pixelData[3];
1094          // Scale to [0.0 ... 1.0]
1095          fc = ic / scale;
1096          fm = im / scale;
1097          fy = iy / scale;
1098          fb = ib / scale;
1099          fa = 1.0;
1100        }
1101
1102      return [NSColor colorWithDeviceCyan: fc
1103                      magenta: fm
1104                      yellow: fy
1105                      black: fb
1106                      alpha: fa];
1107    }
1108
1109  return nil;
1110}
1111
1112/**
1113 * Sets the color of pixel (x,y), where (0,0) is the top-left pixel in the
1114 * image.
1115 */
1116- (void) setColor: (NSColor*)color atX: (NSInteger)x y: (NSInteger)y
1117{
1118  NSUInteger pixelData[5];
1119  NSColor *conv;
1120
1121  if (x < 0 || y < 0 || x >= _pixelsWide || y >= _pixelsHigh)
1122    {
1123      // outside
1124      return;
1125    }
1126
1127  conv = [color colorUsingColorSpaceName: _colorSpace];
1128  if (!conv)
1129    {
1130      return;
1131    }
1132
1133  if ([_colorSpace isEqualToString: NSCalibratedRGBColorSpace]
1134      || [_colorSpace isEqualToString: NSDeviceRGBColorSpace])
1135    {
1136      NSUInteger ir, ig, ib, ia;
1137      CGFloat fr, fg, fb, fa;
1138      CGFloat scale;
1139
1140      scale = (CGFloat)((1 << _bitsPerSample) - 1);
1141      [conv getRed: &fr green: &fg blue: &fb alpha: &fa];
1142      if(_hasAlpha)
1143        {
1144          // Scale and premultiply alpha
1145          if (_format & NSAlphaNonpremultipliedBitmapFormat)
1146            {
1147              ir = scale * fr;
1148              ig = scale * fg;
1149              ib = scale * fb;
1150            }
1151          else
1152            {
1153              ir = scale * fr * fa;
1154              ig = scale * fg * fa;
1155              ib = scale * fb * fa;
1156            }
1157          ia = scale * fa;
1158
1159          // This order depends on the bitmap format
1160          if (_format & NSAlphaFirstBitmapFormat)
1161            {
1162              pixelData[0] = ia;
1163              pixelData[1] = ir;
1164              pixelData[2] = ig;
1165              pixelData[3] = ib;
1166            }
1167          else
1168            {
1169              pixelData[0] = ir;
1170              pixelData[1] = ig;
1171              pixelData[2] = ib;
1172              pixelData[3] = ia;
1173            }
1174        }
1175      else
1176        {
1177          // Scale
1178          ir = scale * fr;
1179          ig = scale * fg;
1180          ib = scale * fb;
1181          // This order depends on the bitmap format
1182          pixelData[0] = ir;
1183          pixelData[1] = ig;
1184          pixelData[2] = ib;
1185        }
1186    }
1187  else if ([_colorSpace isEqual: NSDeviceWhiteColorSpace]
1188           || [_colorSpace isEqual: NSCalibratedWhiteColorSpace])
1189    {
1190      NSUInteger iw, ia;
1191      CGFloat fw, fa;
1192      CGFloat scale;
1193
1194      scale = (CGFloat)((1 << _bitsPerSample) - 1);
1195      [conv getWhite: &fw alpha: &fa];
1196      if (_hasAlpha)
1197        {
1198          if (_format & NSAlphaNonpremultipliedBitmapFormat)
1199            {
1200              iw = scale * fw;
1201            }
1202          else
1203            {
1204              iw = scale * fw * fa;
1205            }
1206          ia = scale * fa;
1207
1208          // This order depends on the bitmap format
1209          if (_format & NSAlphaFirstBitmapFormat)
1210            {
1211              pixelData[0] = ia;
1212              pixelData[1] = iw;
1213            }
1214          else
1215            {
1216              pixelData[0] = iw;
1217              pixelData[1] = ia;
1218            }
1219        }
1220      else
1221        {
1222          iw = scale * fw;
1223          pixelData[0] = iw;
1224        }
1225    }
1226  else if ([_colorSpace isEqual: NSDeviceBlackColorSpace]
1227           || [_colorSpace isEqual: NSCalibratedBlackColorSpace])
1228    {
1229      NSUInteger iw, ia;
1230      CGFloat fw, fa;
1231      CGFloat scale;
1232
1233      scale = (CGFloat)((1 << _bitsPerSample) - 1);
1234      [conv getWhite: &fw alpha: &fa];
1235      if (_hasAlpha)
1236        {
1237          if (_format & NSAlphaNonpremultipliedBitmapFormat)
1238            {
1239              iw = scale * (1 - fw);
1240            }
1241          else
1242            {
1243              iw = scale * (1 - fw) * fa;
1244            }
1245          ia = scale * fa;
1246
1247          // This order depends on the bitmap format
1248          if (_format & NSAlphaFirstBitmapFormat)
1249            {
1250              pixelData[0] = ia;
1251              pixelData[1] = iw;
1252            }
1253          else
1254            {
1255              pixelData[0] = iw;
1256              pixelData[1] = ia;
1257            }
1258        }
1259      else
1260        {
1261          iw = scale * (1 - fw);
1262          pixelData[0] = iw;
1263        }
1264    }
1265  else if ([_colorSpace isEqual: NSDeviceCMYKColorSpace])
1266    {
1267      NSUInteger ic, im, iy, ib, ia;
1268      CGFloat fc, fm, fy, fb, fa;
1269      CGFloat scale;
1270
1271      scale = (CGFloat)((1 << _bitsPerSample) - 1);
1272      [conv getCyan: &fc magenta: &fm yellow: &fy black: &fb alpha: &fa];
1273      if(_hasAlpha)
1274        {
1275          if (_format & NSAlphaNonpremultipliedBitmapFormat)
1276            {
1277              ic = scale * fc;
1278              im = scale * fm;
1279              iy = scale * fy;
1280              ib = scale * fb;
1281            }
1282          else
1283            {
1284              ic = scale * fc * fa;
1285              im = scale * fm * fa;
1286              iy = scale * fy * fa;
1287              ib = scale * fb * fa;
1288            }
1289          ia = scale * fa;
1290
1291          // This order depends on the bitmap format
1292          if (_format & NSAlphaFirstBitmapFormat)
1293            {
1294              pixelData[0] = ia;
1295              pixelData[1] = ic;
1296              pixelData[2] = im;
1297              pixelData[3] = iy;
1298              pixelData[4] = ib;
1299            }
1300          else
1301            {
1302              pixelData[0] = ic;
1303              pixelData[1] = im;
1304              pixelData[2] = iy;
1305              pixelData[3] = ib;
1306              pixelData[4] = ia;
1307            }
1308        }
1309      else
1310        {
1311          ic = scale * fc;
1312          im = scale * fm;
1313          iy = scale * fy;
1314          ib = scale * fb;
1315          // This order depends on the bitmap format
1316          pixelData[0] = ic;
1317          pixelData[1] = im;
1318          pixelData[2] = iy;
1319          pixelData[3] = ib;
1320        }
1321    }
1322  else
1323    {
1324      // FIXME: Other colour spaces not implemented
1325      return;
1326    }
1327
1328  [self setPixel: pixelData atX: x y: y];
1329}
1330
1331/** Draws the image in the current window according the information
1332    from the current gState, including information about the current
1333    point, scaling, etc.  */
1334- (BOOL) draw
1335{
1336  NSRect irect = NSMakeRect(0, 0, _size.width, _size.height);
1337  NSGraphicsContext *ctxt = GSCurrentContext();
1338
1339  [self _premultiply];
1340  [ctxt GSDrawImage: irect : self];
1341  return YES;
1342}
1343
1344//
1345// Producing a TIFF Representation of the Image
1346//
1347/** Produces an NSData object containing a TIFF representation of all
1348   the images stored in anArray.  BUGS: Currently this only works if the
1349   images are NSBitmapImageRep objects.  */
1350+ (NSData*) TIFFRepresentationOfImageRepsInArray: (NSArray *)anArray
1351{
1352  NSEnumerator *enumerator = [anArray objectEnumerator];
1353  NSImageRep *rep;
1354  TIFF *image;
1355  NSTiffInfo info;
1356  char *bytes = 0;
1357  long length = 0;
1358  int num = 0;
1359  NSData *data;
1360
1361  image = NSTiffOpenDataWrite(&bytes, &length);
1362  if (image == 0)
1363    {
1364      [NSException raise: NSTIFFException
1365		   format: @"Opening data stream for writing"];
1366    }
1367
1368  while ((rep = [enumerator nextObject]) != nil)
1369    {
1370      if ([rep isKindOfClass: self])
1371        {
1372          NSTIFFCompression compression;
1373          float factor;
1374          NSBitmapImageRep *bitmap = (NSBitmapImageRep*)rep;
1375
1376          [bitmap getCompression: &compression
1377                          factor: &factor];
1378          [bitmap _fillTIFFInfo: &info
1379               usingCompression: compression
1380                         factor: factor];
1381          info.imageNumber = num++;
1382          info.numImages = [anArray count];
1383          info.subfileType = FILETYPE_PAGE;
1384          if (NSTiffWrite(image, &info, [bitmap bitmapData]) != 0)
1385            {
1386              [NSException raise: NSTIFFException format: @"Writing data"];
1387            }
1388	}
1389    }
1390
1391  NSTiffClose(image);
1392  data = [NSData dataWithBytesNoCopy: bytes length: length];
1393  if (num > 0)
1394    {
1395      return data;
1396    }
1397  else
1398    {
1399      // FIXME: Not sure wether this is the correct behaviour, at least it was
1400      // the old one of this method.
1401      return nil;
1402    }
1403}
1404
1405/** Produces an NSData object containing a TIFF representation of all
1406   the images stored in anArray. The image is compressed according to
1407   the compression type and factor. BUGS: Currently this only works if
1408   the images are NSBitmapImageRep objects. */
1409+ (NSData*) TIFFRepresentationOfImageRepsInArray: (NSArray *)anArray
1410				usingCompression: (NSTIFFCompression)compression
1411					  factor: (float)factor
1412{
1413  NSEnumerator *enumerator = [anArray objectEnumerator];
1414  NSImageRep *rep;
1415  NSTiffInfo info;
1416  TIFF *image;
1417  char *bytes = 0;
1418  long length = 0;
1419  int num = 0;
1420  NSData *data;
1421
1422  image = NSTiffOpenDataWrite(&bytes, &length);
1423  if (image == 0)
1424    {
1425      [NSException raise: NSTIFFException
1426		   format: @"Opening data stream for writing"];
1427    }
1428
1429  while ((rep = [enumerator nextObject]) != nil)
1430    {
1431      if ([rep isKindOfClass: self])
1432        {
1433          [(NSBitmapImageRep*)rep _fillTIFFInfo: &info
1434                               usingCompression: compression
1435                                         factor: factor];
1436          info.imageNumber = num++;
1437          info.numImages = [anArray count];
1438          info.subfileType = FILETYPE_PAGE;
1439          if (NSTiffWrite(image, &info, [(NSBitmapImageRep*)rep bitmapData]) != 0)
1440            {
1441              [NSException raise: NSTIFFException format: @"Writing data"];
1442            }
1443	}
1444    }
1445
1446  NSTiffClose(image);
1447  data = [NSData dataWithBytesNoCopy: bytes length: length];
1448  if (num > 0)
1449    {
1450      return data;
1451    }
1452  else
1453    {
1454      // FIXME: Not sure wether this is the correct behaviour, at least it was
1455      // the old one of this method.
1456      return nil;
1457    }
1458}
1459
1460/** Returns an NSData object containing a TIFF representation of the
1461    receiver.  */
1462- (NSData*) TIFFRepresentation
1463{
1464  if ([self canBeCompressedUsing: _compression] == NO)
1465    {
1466      [self setCompression: NSTIFFCompressionNone factor: 0];
1467    }
1468  return [self TIFFRepresentationUsingCompression: _compression
1469	       factor: _comp_factor];
1470}
1471
1472/** Returns an NSData object containing a TIFF representation of the
1473    receiver. The TIFF data is compressed using compresssion type
1474    and factor.  */
1475- (NSData*) TIFFRepresentationUsingCompression: (NSTIFFCompression)compression
1476					factor: (float)factor
1477{
1478  NSTiffInfo	info;
1479  TIFF		*image;
1480  char		*bytes = 0;
1481  long		length = 0;
1482
1483  image = NSTiffOpenDataWrite(&bytes, &length);
1484  if (image == 0)
1485    {
1486      [NSException raise: NSTIFFException
1487		   format: @"Opening data stream for writing"];
1488    }
1489
1490  [self _fillTIFFInfo: &info
1491     usingCompression: compression
1492               factor: factor];
1493  if (NSTiffWrite(image, &info, [self bitmapData]) != 0)
1494    {
1495      [NSException raise: NSTIFFException format: @"Writing data"];
1496    }
1497  NSTiffClose(image);
1498  return [NSData dataWithBytesNoCopy: bytes length: length];
1499}
1500
1501/** <p> Returns a data object in the selected format with multiple images.</p>
1502  <p> See Also: -setProperty:withValue: for the options supported in the properties.</p>
1503  <p> FIXME: returns only the first image in the array, and only works for
1504  NSBitmapImageRep or subclasses thereof. </p>
1505*/
1506+ (NSData *)representationOfImageRepsInArray:(NSArray *)imageReps
1507				   usingType:(NSBitmapImageFileType)storageType
1508				  properties:(NSDictionary *)properties
1509{
1510  // Partial implementation only returns data for the first imageRep in the array
1511  // and only works for NSBitmapImageRep or subclasses thereof.
1512  //FIXME: This only outputs one of the ImageReps
1513  NSEnumerator *enumerator = [imageReps objectEnumerator];
1514  NSImageRep *rep;
1515
1516  if (storageType == NSTIFFFileType)
1517    {
1518      NSNumber *comp_property = [properties objectForKey: NSImageCompressionMethod];
1519      NSNumber *factor_property = [properties objectForKey: NSImageCompressionFactor];
1520
1521      if ((comp_property != nil) && (factor_property != nil))
1522        {
1523          float factor = [factor_property floatValue];
1524          NSTIFFCompression compression = [comp_property unsignedShortValue];
1525
1526          return [self TIFFRepresentationOfImageRepsInArray: imageReps
1527                                           usingCompression: compression
1528                                                     factor: factor];
1529        }
1530      else
1531        {
1532          return [self TIFFRepresentationOfImageRepsInArray: imageReps];
1533        }
1534    }
1535  else
1536    {
1537      while ((rep = [enumerator nextObject]) != nil)
1538        {
1539          if ([rep isKindOfClass: self])
1540            {
1541              return [(NSBitmapImageRep*)rep representationUsingType: storageType
1542                                                          properties: properties];
1543            }
1544        }
1545    }
1546
1547  return nil;
1548}
1549
1550/** <p> Returns a data object in one of the supported bitmap graphics file types.
1551  A limited set of options may be passed via the properties. If the passed in properties is nil,
1552  it falls back to the options set with -setProperty:withValue:. File types not yet
1553  implemented return nil and log an error message.</p>
1554  <p> See Also: -setProperty:withValue: for supported options in the properties. </p>
1555*/
1556- (NSData *)representationUsingType:(NSBitmapImageFileType)storageType
1557			 properties:(NSDictionary *)properties
1558{
1559  // if it exists, the passed in properties takes precedence over the internal _properties
1560  NSDictionary * __properties;
1561  __properties = (properties)? properties : (NSDictionary *)_properties;
1562
1563  switch (storageType)
1564  {
1565    case NSTIFFFileType:
1566    {
1567      NSNumber *property;
1568      float factor = _comp_factor;
1569      NSTIFFCompression compression = _compression;
1570      if ((property = [__properties objectForKey: NSImageCompressionMethod]))
1571        compression =  [property unsignedShortValue];
1572      if ((property = [__properties objectForKey: NSImageCompressionFactor]))
1573        factor = [property floatValue];
1574      if ([self canBeCompressedUsing: compression] == NO)
1575        {
1576          factor = 0.0;
1577          compression = NSTIFFCompressionNone;
1578        }
1579      return [self TIFFRepresentationUsingCompression: compression factor: factor];
1580    }
1581
1582    case NSBMPFileType:
1583      NSLog(@"BMP representation is not yet implemented");
1584      return nil;
1585
1586    case NSGIFFileType:
1587      return [self _GIFRepresentationWithProperties: __properties
1588                                       errorMessage: NULL];
1589
1590    case NSJPEGFileType:
1591      return [self _JPEGRepresentationWithProperties: __properties
1592                                        errorMessage: NULL];
1593
1594    case NSPNGFileType:
1595      return [self _PNGRepresentationWithProperties: __properties];
1596
1597    case NSJPEG2000FileType:
1598      NSLog(@"JPEG2000 representation is not yet implemented");
1599      return nil;
1600  }
1601  return nil;
1602}
1603
1604//
1605// Setting and Checking Compression Types
1606//
1607/** Returns a C-array of available TIFF compression types.
1608 */
1609+ (void) getTIFFCompressionTypes: (const NSTIFFCompression **)list
1610			   count: (NSInteger *)numTypes
1611{
1612  // the GNUstep supported types
1613  static NSTIFFCompression	types[] = {
1614    NSTIFFCompressionNone,
1615    NSTIFFCompressionCCITTFAX3,
1616    NSTIFFCompressionCCITTFAX4,
1617    NSTIFFCompressionLZW,
1618    NSTIFFCompressionJPEG,
1619    NSTIFFCompressionNEXT,
1620    NSTIFFCompressionPackBits,
1621    NSTIFFCompressionOldJPEG
1622  };
1623
1624  // check with libtiff to see what is really available
1625  NSInteger i, j;
1626  static NSTIFFCompression checkedTypes[8];
1627  for (i = 0, j = 0; i < 8; i++)
1628  {
1629    if (NSTiffIsCodecConfigured([NSBitmapImageRep _localFromCompressionType: types[i]]))
1630    {
1631      checkedTypes[j] = types[i];
1632      j++;
1633    }
1634  }
1635  if (list)
1636    *list = checkedTypes;
1637  if (numTypes)
1638    *numTypes = j;
1639}
1640
1641/** Returns a localized string describing a TIFF compression type. */
1642+ (NSString*) localizedNameForTIFFCompressionType: (NSTIFFCompression)type
1643{
1644  switch (type)
1645    {
1646      case NSTIFFCompressionNone: return _(@"No Compression");
1647      case NSTIFFCompressionCCITTFAX3: return _(@"CCITTFAX3 Compression");
1648      case NSTIFFCompressionCCITTFAX4: return _(@"CCITTFAX4 Compression");
1649      case NSTIFFCompressionLZW: return _(@"LZW Compression");
1650      case NSTIFFCompressionJPEG: return _(@"JPEG Compression");
1651      case NSTIFFCompressionNEXT: return _(@"NEXT Compression");
1652      case NSTIFFCompressionPackBits: return _(@"PackBits Compression");
1653      case NSTIFFCompressionOldJPEG: return _(@"Old JPEG Compression");
1654      default: return nil;
1655    }
1656}
1657
1658/** Returns YES if the receiver can be stored in a representation
1659    compressed using the compression type.  */
1660- (BOOL) canBeCompressedUsing: (NSTIFFCompression)compression
1661{
1662  BOOL does;
1663  int codecConf =
1664    NSTiffIsCodecConfigured([NSBitmapImageRep _localFromCompressionType: compression]);
1665  switch (compression)
1666    {
1667      case NSTIFFCompressionCCITTFAX3:
1668      case NSTIFFCompressionCCITTFAX4:
1669	if (_numColors == 1 && _bitsPerSample == 1 && codecConf != 0)
1670	  does = YES;
1671	else
1672	  does = NO;
1673	break;
1674
1675      case NSTIFFCompressionLZW:
1676      case NSTIFFCompressionNone:
1677      case NSTIFFCompressionJPEG:	// this is a GNUstep extension; Cocoa does not support
1678      case NSTIFFCompressionPackBits:
1679      case NSTIFFCompressionOldJPEG:
1680      case NSTIFFCompressionNEXT:
1681      default:
1682	does = (codecConf != 0);
1683    }
1684  return does;
1685}
1686
1687/** Returns the receivers compression and compression factor, which is
1688    set either when the image is read in or by -setCompression:factor:.
1689    Factor is ignored in many compression schemes. For JPEG compression,
1690    factor can be any value from 0 to 1, with 1 being the maximum
1691    quality.  */
1692- (void) getCompression: (NSTIFFCompression*)compression
1693		 factor: (float*)factor
1694{
1695  *compression = _compression;
1696  *factor = _comp_factor;
1697}
1698
1699- (void) setCompression: (NSTIFFCompression)compression
1700		 factor: (float)factor
1701{
1702  _compression = compression;
1703  _comp_factor = factor;
1704}
1705
1706/** <p> Properties are key-value pairs associated with the representation. Arbitrary
1707  key-value pairs may be set. If the value is nil, the key is erased from properties.
1708  There are standard keys that are used to pass information
1709  and options related to the standard file types that may be read from or written to.
1710  Certain properties are automatically set when reading in image data.
1711  Certain properties may be set by the user prior to writing image data in order to set options
1712  for the data format. </p>
1713  <deflist>
1714    <term> NSImageCompressionMethod </term>
1715    <desc> NSNumber; automatically set when reading TIFF data; writing TIFF data </desc>
1716    <term> NSImageCompressionFactor </term>
1717    <desc> NSNumber 0.0 to 1.0; writing JPEG data
1718    (GNUstep extension: JPEG-compressed TIFFs too) </desc>
1719    <term> NSImageProgressive </term>
1720    <desc> NSNumber boolean; automatically set when reading JPEG data; writing JPEG data.
1721    Note: progressive display is not supported in GNUstep at this time. </desc>
1722    <term> NSImageInterlaced </term>
1723    <desc> NSNumber boolean; only for writing PNG data </desc>
1724    <term> NSImageGamma </term>
1725    <desc> NSNumber 0.0 to 1.0; only for reading or writing PNG data </desc>
1726    <term> NSImageRGBColorTable </term>
1727    <desc> NSData; automatically set when reading GIF data; writing GIF data </desc>
1728    <term> NSImageFrameCount </term>
1729    <desc> NSNumber integer; automatically set when reading animated GIF data.
1730    Not currently implemented. </desc>
1731    <term> NSImageCurrentFrame </term>
1732    <desc> NSNumber integer; only for animated GIF files. Not currently implemented. </desc>
1733    <term> NSImageCurrentFrameDuration </term>
1734    <desc> NSNumber float; automatically set when reading animated GIF data </desc>
1735    <term> NSImageLoopCount </term>
1736    <desc> NSNumber integer; automatically set when reading animated GIF data </desc>
1737    <term> NSImageDitherTranparency </term>
1738    <desc> NSNumber boolean; only for writing GIF data. Not currently supported. </desc>
1739  </deflist>
1740*/
1741- (void)setProperty:(NSString *)property withValue:(id)value
1742{
1743  if (value)
1744  {
1745    [_properties setObject: value forKey: property];
1746  }
1747  else  // clear the property
1748  {
1749    [_properties removeObjectForKey: property];
1750  }
1751}
1752
1753/** Returns the value of a property */
1754- (id)valueForProperty:(NSString *)property
1755{
1756  return [_properties objectForKey: property];
1757}
1758
1759// NSCopying protocol
1760- (id) copyWithZone: (NSZone *)zone
1761{
1762  NSBitmapImageRep	*copy;
1763
1764  copy = (NSBitmapImageRep*)[super copyWithZone: zone];
1765
1766  copy->_properties = [_properties mutableCopyWithZone: zone];
1767  copy->_imageData = [_imageData mutableCopyWithZone: zone];
1768  copy->_imagePlanes = NSZoneMalloc(zone, sizeof(unsigned char*) * MAX_PLANES);
1769  if (_imageData == nil)
1770    {
1771      memcpy(copy->_imagePlanes, _imagePlanes, sizeof(unsigned char*) * MAX_PLANES);
1772    }
1773  else
1774    {
1775      unsigned char *bits;
1776      unsigned int i;
1777
1778      bits = (unsigned char *)[copy->_imageData bytes];
1779      copy->_imagePlanes[0] = bits;
1780      if (_isPlanar)
1781	{
1782	  for (i = 1; i < _numColors; i++)
1783	    copy->_imagePlanes[i] = bits + i * _bytesPerRow * _pixelsHigh;
1784	  for (i = _numColors; i < MAX_PLANES; i++)
1785	    copy->_imagePlanes[i] = NULL;
1786	}
1787      else
1788	{
1789	  for (i = 1; i < MAX_PLANES; i++)
1790	    copy->_imagePlanes[i] = NULL;
1791	}
1792    }
1793
1794  return copy;
1795}
1796
1797//
1798// NSCoding protocol
1799//
1800- (void) encodeWithCoder: (NSCoder*)aCoder
1801{
1802  NSData *data = [self TIFFRepresentation];
1803
1804  [super encodeWithCoder: aCoder];
1805  if ([aCoder allowsKeyedCoding])
1806    {
1807      [aCoder encodeObject: data forKey: @"NSTIFFRepresentation"];
1808    }
1809  else
1810    {
1811      [aCoder encodeObject: data];
1812    }
1813}
1814
1815- (id) initWithCoder: (NSCoder*)aDecoder
1816{
1817  NSData	*data;
1818
1819  self = [super initWithCoder: aDecoder];
1820  if ([aDecoder allowsKeyedCoding])
1821    {
1822      data = [aDecoder decodeObjectForKey: @"NSTIFFRepresentation"];
1823    }
1824  else
1825    {
1826      data = [aDecoder decodeObject];
1827    }
1828  return [self initWithData: data];
1829}
1830
1831@end
1832
1833@implementation NSBitmapImageRep (GSPrivate)
1834
1835+ (int) _localFromCompressionType: (NSTIFFCompression)type
1836{
1837  switch (type)
1838    {
1839    case NSTIFFCompressionNone: return COMPRESSION_NONE;
1840    case NSTIFFCompressionCCITTFAX3: return COMPRESSION_CCITTFAX3;
1841    case NSTIFFCompressionCCITTFAX4: return COMPRESSION_CCITTFAX4;
1842    case NSTIFFCompressionLZW: return COMPRESSION_LZW;
1843    case NSTIFFCompressionJPEG: return COMPRESSION_JPEG;
1844    case NSTIFFCompressionNEXT: return COMPRESSION_NEXT;
1845    case NSTIFFCompressionPackBits: return COMPRESSION_PACKBITS;
1846    case NSTIFFCompressionOldJPEG: return COMPRESSION_OJPEG;
1847    default:
1848      break;
1849    }
1850  return COMPRESSION_NONE;
1851}
1852
1853+ (NSTIFFCompression) _compressionTypeFromLocal: (int)type
1854{
1855  switch (type)
1856    {
1857    case COMPRESSION_NONE: return NSTIFFCompressionNone;
1858    case COMPRESSION_CCITTFAX3: return NSTIFFCompressionCCITTFAX3;
1859    case COMPRESSION_CCITTFAX4: return NSTIFFCompressionCCITTFAX4;
1860    case COMPRESSION_LZW: return NSTIFFCompressionLZW;
1861    case COMPRESSION_JPEG: return NSTIFFCompressionJPEG;
1862    case COMPRESSION_NEXT: return NSTIFFCompressionNEXT;
1863    case COMPRESSION_PACKBITS: return NSTIFFCompressionPackBits;
1864    case COMPRESSION_OJPEG: return NSTIFFCompressionOldJPEG;
1865    default:
1866      break;
1867   }
1868  return NSTIFFCompressionNone;
1869}
1870
1871+ (BOOL) _bitmapIsTIFF: (NSData *)data
1872{
1873  TIFF *image = NSTiffOpenDataRead((char *)[data bytes], [data length]);
1874
1875  if (image != NULL)
1876    {
1877      NSTiffClose(image);
1878      return YES;
1879    }
1880  else
1881    {
1882      return NO;
1883    }
1884}
1885
1886+ (NSArray*) _imageRepsWithTIFFData: (NSData *)imageData
1887{
1888  int		 i, images;
1889  TIFF		 *image;
1890  NSMutableArray *array;
1891
1892  image = NSTiffOpenDataRead((char *)[imageData bytes], [imageData length]);
1893  if (image == NULL)
1894    {
1895      NSLog(@"NSBitmapImageRep: unable to parse TIFF data");
1896      return [NSArray array];
1897    }
1898
1899  images = NSTiffGetImageCount(image);
1900  NSDebugLLog(@"NSImage", @"Image contains %d directories", images);
1901  array = [NSMutableArray arrayWithCapacity: images];
1902  for (i = 0; i < images; i++)
1903    {
1904      NSBitmapImageRep* imageRep;
1905      imageRep = [[self alloc] _initFromTIFFImage: image number: i];
1906      if (imageRep)
1907	{
1908	  [array addObject: imageRep];
1909          RELEASE(imageRep);
1910	}
1911    }
1912  NSTiffClose(image);
1913
1914  return array;
1915}
1916
1917- (NSBitmapImageRep *) _initBitmapFromTIFF: (NSData *)imageData
1918{
1919  TIFF *image = NSTiffOpenDataRead((char *)[imageData bytes], [imageData length]);
1920
1921  if (image == NULL)
1922    {
1923      RELEASE(self);
1924      NSLog(@"Tiff read invalid TIFF info from data");
1925      return nil;
1926    }
1927
1928  [self _initFromTIFFImage: image number: -1];
1929  NSTiffClose(image);
1930  return self;
1931}
1932
1933/* Given a TIFF image (from the libtiff library), load the image information
1934   into our data structure.  Reads the specified image. */
1935- (NSBitmapImageRep *) _initFromTIFFImage: (TIFF *)image number: (int)imageNumber
1936{
1937  NSString* space;
1938  NSTiffInfo* info;
1939
1940  /* Seek to the correct image and get the dictionary information */
1941  info = NSTiffGetInfo(imageNumber, image);
1942  if (!info)
1943    {
1944      RELEASE(self);
1945      NSLog(@"Tiff read invalid TIFF info in directory %d", imageNumber);
1946      return nil;
1947    }
1948
1949  /* 8-bit RGB will be converted to 24-bit by the tiff routines, so account
1950     for this. */
1951  space = nil;
1952  switch(info->photoInterp)
1953    {
1954    case PHOTOMETRIC_MINISBLACK: space = NSDeviceWhiteColorSpace; break;
1955    case PHOTOMETRIC_MINISWHITE: space = NSDeviceBlackColorSpace; break;
1956    case PHOTOMETRIC_RGB: space = NSDeviceRGBColorSpace; break;
1957    case PHOTOMETRIC_PALETTE:
1958      space = NSDeviceRGBColorSpace;
1959      info->samplesPerPixel = 3;
1960      break;
1961    default:
1962      break;
1963    }
1964
1965  [self initWithBitmapDataPlanes: NULL
1966        pixelsWide: info->width
1967        pixelsHigh: info->height
1968        bitsPerSample: info->bitsPerSample
1969        samplesPerPixel: info->samplesPerPixel
1970        hasAlpha: (info->extraSamples > 0)
1971        isPlanar: (info->planarConfig == PLANARCONFIG_SEPARATE)
1972        colorSpaceName: space
1973        bitmapFormat: (info->assocAlpha ? 0 :
1974                       NSAlphaNonpremultipliedBitmapFormat)
1975        bytesPerRow: 0
1976        bitsPerPixel: 0];
1977  _compression = [NSBitmapImageRep _compressionTypeFromLocal: info->compression];
1978  _comp_factor = (((float)info->quality)/100.0);
1979
1980  // Note that Cocoa does not do this, even though the docs say it should
1981  [_properties setObject: [NSNumber numberWithUnsignedShort: _compression]
1982                  forKey: NSImageCompressionMethod];
1983  [_properties setObject: [NSNumber numberWithFloat: _comp_factor]
1984                  forKey: NSImageCompressionFactor];
1985
1986  if (info->xdpi > 0 && info->xdpi != 72 &&
1987      info->ydpi > 0 && info->ydpi != 72)
1988    {
1989      NSSize pointSize = NSMakeSize((double)info->width * (72.0 / (double)info->xdpi),
1990				    (double)info->height * (72.0 / (double)info->ydpi));
1991      [self setSize: pointSize];
1992    }
1993
1994  if (NSTiffRead(image, info, [self bitmapData]))
1995    {
1996      free(info);
1997      RELEASE(self);
1998      NSLog(@"Tiff read invalid TIFF image data in directory %d", imageNumber);
1999      return nil;
2000    }
2001  free(info);
2002
2003  return self;
2004}
2005
2006- (void) _fillTIFFInfo: (NSTiffInfo*)info
2007      usingCompression: (NSTIFFCompression)type
2008                factor: (float)factor
2009{
2010  info->numImages = 1;
2011  info->imageNumber = 0;
2012  info->subfileType = 0;
2013  info->width = _pixelsWide;
2014  info->height = _pixelsHigh;
2015  info->bitsPerSample = _bitsPerSample;
2016  info->samplesPerPixel = _numColors;
2017
2018  // resolution/density
2019  info->xdpi = 0;
2020  info->ydpi = 0;
2021  if (_pixelsWide != (int)(_size.width) || _pixelsHigh != (int)(_size.height))
2022    {
2023      float x_density, y_density;
2024      x_density = _pixelsWide * 72 / _size.width;
2025      y_density = _pixelsHigh * 72 / _size.height;
2026      info->xdpi = x_density;
2027      info->ydpi = y_density;
2028    }
2029
2030  if (_isPlanar)
2031    info->planarConfig = PLANARCONFIG_SEPARATE;
2032  else
2033    info->planarConfig = PLANARCONFIG_CONTIG;
2034
2035  if ([_colorSpace isEqual: NSDeviceRGBColorSpace]
2036      || [_colorSpace isEqual: NSCalibratedRGBColorSpace])
2037    info->photoInterp = PHOTOMETRIC_RGB;
2038  else if ([_colorSpace isEqual: NSDeviceWhiteColorSpace]
2039	   || [_colorSpace isEqual: NSCalibratedWhiteColorSpace])
2040    info->photoInterp = PHOTOMETRIC_MINISBLACK;
2041  else if ([_colorSpace isEqual: NSDeviceBlackColorSpace]
2042	   || [_colorSpace isEqual: NSCalibratedBlackColorSpace])
2043    info->photoInterp = PHOTOMETRIC_MINISWHITE;
2044  else
2045    {
2046      NSWarnMLog(@"Unknown colorspace %@.", _colorSpace);
2047      info->photoInterp = PHOTOMETRIC_RGB;
2048    }
2049
2050  info->extraSamples = (_hasAlpha) ? 1 : 0;
2051  info->assocAlpha = (_format & NSAlphaNonpremultipliedBitmapFormat) ? 0 : 1;
2052
2053  if ([self canBeCompressedUsing: type] == NO)
2054    {
2055      type = NSTIFFCompressionNone;
2056      factor = 0;
2057    }
2058
2059  info->compression = [NSBitmapImageRep _localFromCompressionType: type];
2060  if (factor < 0)
2061    factor = 0;
2062  if (factor > 1)
2063    factor = 1;
2064  info->quality = factor * 100;
2065  info->error = 0;
2066}
2067
2068- (void) _premultiply
2069{
2070  NSInteger x, y;
2071  NSUInteger pixelData[5];
2072  NSInteger start, end, i, ai;
2073  SEL getPSel = @selector(getPixel:atX:y:);
2074  SEL setPSel = @selector(setPixel:atX:y:);
2075  IMP getP = [self methodForSelector: getPSel];
2076  IMP setP = [self methodForSelector: setPSel];
2077
2078  if (!_hasAlpha || !(_format & NSAlphaNonpremultipliedBitmapFormat))
2079    return;
2080
2081  if (_format & NSAlphaFirstBitmapFormat)
2082    {
2083      ai = 0;
2084      start = 1;
2085      end = _numColors;
2086    }
2087  else
2088    {
2089      ai = _numColors - 1;
2090      start = 0;
2091      end = _numColors - 1;
2092    }
2093
2094  if (_bitsPerSample == 8)
2095    {
2096      if (!_isPlanar)
2097        {
2098          // Optimize for the most common case
2099          NSUInteger a;
2100          NSInteger offset;
2101          NSInteger line_offset;
2102
2103          for (y = 0; y < _pixelsHigh; y++)
2104            {
2105              line_offset = _bytesPerRow * y;
2106              for (x = 0; x < _pixelsWide; x++)
2107                {
2108                  offset = (_bitsPerPixel * x) / 8 + line_offset;
2109                  a = _imagePlanes[0][offset + ai];
2110                  if (a != 255)
2111                    {
2112                      if (a == 0)
2113                        {
2114                          for (i = start; i < end; i++)
2115                            {
2116                              _imagePlanes[0][offset + i] = 0;
2117                            }
2118                        }
2119                      else
2120                        {
2121                          for (i = start; i < end; i++)
2122                            {
2123                              NSUInteger v = _imagePlanes[0][offset + i];
2124                              NSUInteger t = a * v + 0x80;
2125
2126                              v = ((t >> 8) + t) >> 8;
2127                              _imagePlanes[0][offset + i] = v;
2128                            }
2129                        }
2130                    }
2131                }
2132            }
2133        }
2134      else
2135        {
2136          NSUInteger a;
2137
2138          for (y = 0; y < _pixelsHigh; y++)
2139            {
2140              for (x = 0; x < _pixelsWide; x++)
2141                {
2142                  //[self getPixel: pixelData atX: x y: y];
2143                  getP(self, getPSel, pixelData, x, y);
2144                  a = pixelData[ai];
2145                  if (a != 255)
2146                    {
2147                      for (i = start; i < end; i++)
2148                        {
2149                          NSUInteger t = a * pixelData[i] + 0x80;
2150
2151                          pixelData[i] = ((t >> 8) + t) >> 8;
2152                        }
2153                      //[self setPixel: pixelData atX: x y: y];
2154                      setP(self, setPSel, pixelData, x, y);
2155                    }
2156                }
2157            }
2158        }
2159    }
2160  else
2161    {
2162      CGFloat scale;
2163      CGFloat alpha;
2164
2165      scale = (CGFloat)((1 << _bitsPerSample) - 1);
2166      for (y = 0; y < _pixelsHigh; y++)
2167        {
2168          for (x = 0; x < _pixelsWide; x++)
2169            {
2170              //[self getPixel: pixelData atX: x y: y];
2171              getP(self, getPSel, pixelData, x, y);
2172              alpha = pixelData[ai] / scale;
2173              for (i = start; i < end; i++)
2174                {
2175                  pixelData[i] *= alpha;
2176                }
2177              //[self setPixel: pixelData atX: x y: y];
2178              setP(self, setPSel, pixelData, x, y);
2179            }
2180        }
2181    }
2182
2183  _format &= ~NSAlphaNonpremultipliedBitmapFormat;
2184}
2185
2186- (void) _unpremultiply
2187{
2188  NSInteger x, y;
2189  NSUInteger pixelData[5];
2190  NSInteger start, end, i, ai;
2191  SEL getPSel = @selector(getPixel:atX:y:);
2192  SEL setPSel = @selector(setPixel:atX:y:);
2193  IMP getP = [self methodForSelector: getPSel];
2194  IMP setP = [self methodForSelector: setPSel];
2195
2196  if (!_hasAlpha || (_format & NSAlphaNonpremultipliedBitmapFormat))
2197    return;
2198
2199  if (_format & NSAlphaFirstBitmapFormat)
2200    {
2201      ai = 0;
2202      start = 1;
2203      end = _numColors;
2204    }
2205  else
2206    {
2207      ai = _numColors - 1;
2208      start = 0;
2209      end = _numColors - 1;
2210    }
2211
2212  if (_bitsPerSample == 8)
2213    {
2214      if (!_isPlanar)
2215        {
2216          // Optimize for the most common case
2217          NSUInteger a;
2218          NSInteger offset;
2219          NSInteger line_offset;
2220
2221          for (y = 0; y < _pixelsHigh; y++)
2222            {
2223              line_offset = _bytesPerRow * y;
2224              for (x = 0; x < _pixelsWide; x++)
2225                {
2226                  offset = (_bitsPerPixel * x) / 8 + line_offset;
2227                  a = _imagePlanes[0][offset + ai];
2228                  if ((a != 0) && (a != 255))
2229                    {
2230                      for (i = start; i < end; i++)
2231                        {
2232                          NSUInteger v = _imagePlanes[0][offset + i];
2233                          NSUInteger c;
2234
2235                          c = (v * 255) / a;
2236                          if (c >= 255)
2237                            {
2238                              v = 255;
2239                            }
2240                          else
2241                            {
2242                              v = c;
2243                            }
2244
2245                          _imagePlanes[0][offset + i] = v;
2246                        }
2247                    }
2248                }
2249            }
2250        }
2251      else
2252        {
2253          NSUInteger a;
2254
2255          for (y = 0; y < _pixelsHigh; y++)
2256            {
2257              for (x = 0; x < _pixelsWide; x++)
2258                {
2259                  //[self getPixel: pixelData atX: x y: y];
2260                  getP(self, getPSel, pixelData, x, y);
2261                  a = pixelData[ai];
2262                  if ((a != 0) && (a != 255))
2263                    {
2264                      for (i = start; i < end; i++)
2265                        {
2266                          NSUInteger c;
2267
2268                          c = (pixelData[i] * 255) / a;
2269                          if (c >= 255)
2270                            {
2271                              pixelData[i] = 255;
2272                            }
2273                          else
2274                            {
2275                              pixelData[i] = c;
2276                            }
2277                        }
2278                      //[self setPixel: pixelData atX: x y: y];
2279                      setP(self, setPSel, pixelData, x, y);
2280                    }
2281                }
2282            }
2283        }
2284    }
2285  else
2286    {
2287      CGFloat scale;
2288      CGFloat alpha;
2289
2290      scale = (CGFloat)((1 << _bitsPerSample) - 1);
2291      for (y = 0; y < _pixelsHigh; y++)
2292        {
2293          NSUInteger a;
2294
2295          for (x = 0; x < _pixelsWide; x++)
2296            {
2297              //[self getPixel: pixelData atX: x y: y];
2298              getP(self, getPSel, pixelData, x, y);
2299              a = pixelData[ai];
2300              if (a != 0)
2301                {
2302                    alpha = scale / a;
2303                    for (i = start; i < end; i++)
2304                      {
2305                        CGFloat new = pixelData[i] * alpha;
2306
2307                        if (new > scale)
2308                          {
2309                            pixelData[i] = scale;
2310                          }
2311                        else
2312                          {
2313                            pixelData[i] = new;
2314                          }
2315                      }
2316                    //[self setPixel: pixelData atX: x y: y];
2317                    setP(self, setPSel, pixelData, x, y);
2318                }
2319            }
2320        }
2321    }
2322
2323  _format |= NSAlphaNonpremultipliedBitmapFormat;
2324}
2325
2326- (NSBitmapImageRep *) _convertToFormatBitsPerSample: (NSInteger)bps
2327                                     samplesPerPixel: (NSInteger)spp
2328                                            hasAlpha: (BOOL)alpha
2329                                            isPlanar: (BOOL)isPlanar
2330                                      colorSpaceName: (NSString*)colorSpaceName
2331                                        bitmapFormat: (NSBitmapFormat)bitmapFormat
2332                                         bytesPerRow: (NSInteger)rowBytes
2333                                        bitsPerPixel: (NSInteger)pixelBits
2334{
2335  if (!pixelBits)
2336    pixelBits = bps * ((isPlanar) ? 1 : spp);
2337  if (!rowBytes)
2338    rowBytes = ceil((float)_pixelsWide * pixelBits / 8);
2339
2340  // Do we already have the correct format?
2341  if ((bps == _bitsPerSample) && (spp == _numColors)
2342      && (alpha == _hasAlpha) && (isPlanar == _isPlanar)
2343      && (bitmapFormat == _format) && (rowBytes == _bytesPerRow)
2344      && (pixelBits == _bitsPerPixel)
2345      && [_colorSpace isEqualToString: colorSpaceName])
2346    {
2347      return self;
2348    }
2349  else
2350    {
2351      NSBitmapImageRep* new;
2352
2353      new = [[NSBitmapImageRep alloc]
2354                initWithBitmapDataPlanes: NULL
2355                pixelsWide: _pixelsWide
2356                pixelsHigh: _pixelsHigh
2357                bitsPerSample: bps
2358                samplesPerPixel: spp
2359                hasAlpha: alpha
2360                isPlanar: isPlanar
2361                colorSpaceName: colorSpaceName
2362                bitmapFormat: bitmapFormat
2363                bytesPerRow: rowBytes
2364                bitsPerPixel: pixelBits];
2365
2366      if ([_colorSpace isEqualToString: colorSpaceName] ||
2367          ([_colorSpace isEqualToString: NSDeviceRGBColorSpace] &&
2368           [colorSpaceName isEqualToString: NSCalibratedRGBColorSpace]) ||
2369          ([colorSpaceName isEqualToString: NSDeviceRGBColorSpace] &&
2370           [_colorSpace isEqualToString: NSCalibratedRGBColorSpace]))
2371        {
2372          SEL getPSel = @selector(getPixel:atX:y:);
2373          SEL setPSel = @selector(setPixel:atX:y:);
2374          IMP getP = [self methodForSelector: getPSel];
2375          IMP setP = [new methodForSelector: setPSel];
2376          NSUInteger pixelData[5];
2377          NSInteger x, y;
2378          CGFloat _scale;
2379          CGFloat scale;
2380
2381          NSDebugLLog(@"NSImage", @"Converting %@ bitmap data", _colorSpace);
2382
2383          if (_bitsPerSample != bps)
2384            {
2385              _scale = (CGFloat)((1 << _bitsPerSample) - 1);
2386              scale = (CGFloat)((1 << bps) - 1);
2387            }
2388          else
2389            {
2390              _scale = 1.0;
2391              scale = 1.0;
2392            }
2393
2394          for (y = 0; y < _pixelsHigh; y++)
2395            {
2396              for (x = 0; x < _pixelsWide; x++)
2397                {
2398                  NSUInteger iv[4], ia;
2399                  CGFloat fv[4], fa;
2400                  NSInteger i;
2401
2402                 //[self getPixel: pixelData atX: x y: y];
2403                  getP(self, getPSel, pixelData, x, y);
2404
2405                  if (_hasAlpha)
2406                    {
2407                      // This order depends on the bitmap format
2408                      if (_format & NSAlphaFirstBitmapFormat)
2409                        {
2410                          ia = pixelData[0];
2411                          for (i = 0; i < _numColors - 1; i++)
2412                            {
2413                              iv[i] = pixelData[i + 1];
2414                            }
2415                        }
2416                      else
2417                        {
2418                          for (i = 0; i < _numColors - 1; i++)
2419                            {
2420                              iv[i] = pixelData[i];
2421                            }
2422                          ia = pixelData[_numColors - 1];
2423                        }
2424
2425                      // Scale to [0.0 ... 1.0]
2426                      for (i = 0; i < _numColors - 1; i++)
2427                        {
2428                          fv[i] = iv[i] / _scale;
2429                        }
2430                      fa = ia / _scale;
2431
2432                      if ((ia != 0 && fa < 1.0) &&
2433                          (_format & NSAlphaNonpremultipliedBitmapFormat) !=
2434                          (bitmapFormat & NSAlphaNonpremultipliedBitmapFormat))
2435                        {
2436                          if (_format & NSAlphaNonpremultipliedBitmapFormat)
2437                            {
2438                              for (i = 0; i < _numColors - 1; i++)
2439                                {
2440                                  fv[i] = fv[i] * fa;
2441                                }
2442                            }
2443                          else
2444                            {
2445                              for (i = 0; i < _numColors - 1; i++)
2446                                {
2447                                  fv[i] = fv[i] / fa;
2448                                }
2449                            }
2450                        }
2451                    }
2452                  else
2453                    {
2454                      for (i = 0; i < _numColors; i++)
2455                        {
2456                          iv[i] = pixelData[i];
2457                        }
2458                      // Scale to [0.0 ... 1.0]
2459                      for (i = 0; i < _numColors; i++)
2460                        {
2461                          fv[i] = iv[i] / _scale;
2462                        }
2463                      fa = 1.0;
2464                    }
2465
2466                  if (alpha)
2467                    {
2468                      // Scale from [0.0 ... 1.0]
2469                      for (i = 0; i < spp - 1; i++)
2470                        {
2471                          iv[i] = fv[i] * scale;
2472                        }
2473                      ia = fa * scale;
2474
2475                      if (bitmapFormat & NSAlphaFirstBitmapFormat)
2476                        {
2477                          pixelData[0] = ia;
2478                          for (i = 0; i < spp - 1; i++)
2479                            {
2480                              pixelData[i + 1] = iv[i];
2481                            }
2482                        }
2483                      else
2484                        {
2485                          for (i = 0; i < spp - 1; i++)
2486                            {
2487                              pixelData[i] = iv[i];
2488                            }
2489                          pixelData[spp -1] = ia;
2490                        }
2491                    }
2492                  else
2493                    {
2494                      // Scale from [0.0 ... 1.0]
2495                      for (i = 0; i < spp; i++)
2496                        {
2497                          pixelData[i] = fv[i] * scale;
2498                        }
2499                    }
2500
2501                  //[new setPixel: pixelData atX: x y: y];
2502                  setP(new, setPSel, pixelData, x, y);
2503                }
2504            }
2505        }
2506      else if (([colorSpaceName isEqualToString: NSDeviceRGBColorSpace] ||
2507               [colorSpaceName isEqualToString: NSCalibratedRGBColorSpace])
2508          && ([_colorSpace isEqualToString: NSCalibratedWhiteColorSpace] ||
2509              [_colorSpace isEqualToString: NSCalibratedBlackColorSpace] ||
2510              [_colorSpace isEqualToString: NSDeviceWhiteColorSpace] ||
2511              [_colorSpace isEqualToString: NSDeviceBlackColorSpace]))
2512        {
2513          SEL getPSel = @selector(getPixel:atX:y:);
2514          SEL setPSel = @selector(setPixel:atX:y:);
2515          IMP getP = [self methodForSelector: getPSel];
2516          IMP setP = [new methodForSelector: setPSel];
2517          NSUInteger pixelData[4];
2518          NSInteger x, y;
2519          CGFloat _scale;
2520          CGFloat scale;
2521          NSInteger max = (1 << _bitsPerSample) - 1;
2522          BOOL isWhite = [_colorSpace isEqualToString: NSCalibratedWhiteColorSpace]
2523              || [_colorSpace isEqualToString: NSDeviceWhiteColorSpace];
2524
2525          NSDebugLLog(@"NSImage", @"Converting black/white bitmap data");
2526
2527          if (_bitsPerSample != bps)
2528            {
2529              _scale = (CGFloat)((1 << _bitsPerSample) - 1);
2530              scale = (CGFloat)((1 << bps) - 1);
2531            }
2532          else
2533            {
2534              _scale = 1.0;
2535              scale = 1.0;
2536            }
2537
2538          for (y = 0; y < _pixelsHigh; y++)
2539            {
2540              for (x = 0; x < _pixelsWide; x++)
2541                {
2542                  NSUInteger iv, ia;
2543                  CGFloat fv, fa;
2544
2545                 //[self getPixel: pixelData atX: x y: y];
2546                  getP(self, getPSel, pixelData, x, y);
2547
2548                  if (_hasAlpha)
2549                    {
2550                      // This order depends on the bitmap format
2551                      if (_format & NSAlphaFirstBitmapFormat)
2552                        {
2553                          ia = pixelData[0];
2554                          if (isWhite)
2555                            iv = pixelData[1];
2556                          else
2557                            iv = max - pixelData[1];
2558                        }
2559                      else
2560                        {
2561                          if (isWhite)
2562                            iv = pixelData[0];
2563                          else
2564                            iv = max - pixelData[0];
2565                          ia = pixelData[1];
2566                        }
2567
2568                      // Scale to [0.0 ... 1.0]
2569                      fv = iv / _scale;
2570                      fa = ia / _scale;
2571
2572                      if ((ia != 0 && fa < 1.0) &&
2573                          (_format & NSAlphaNonpremultipliedBitmapFormat) !=
2574                          (bitmapFormat & NSAlphaNonpremultipliedBitmapFormat))
2575                        {
2576                          if (_format & NSAlphaNonpremultipliedBitmapFormat)
2577                            {
2578                              fv = fv * fa;
2579                            }
2580                          else
2581                            {
2582                              fv = fv / fa;
2583                            }
2584                        }
2585                    }
2586                  else
2587                    {
2588                      if (isWhite)
2589                        iv = pixelData[0];
2590                      else
2591                        iv = max - pixelData[0];
2592                      // Scale to [0.0 ... 1.0]
2593                      fv = iv / _scale;
2594                      fa = 1.0;
2595                    }
2596
2597                  if (alpha)
2598                    {
2599                      // Scale from [0.0 ... 1.0]
2600                      iv = fv * scale;
2601                      ia = fa * scale;
2602
2603                      if (bitmapFormat & NSAlphaFirstBitmapFormat)
2604                        {
2605                          pixelData[0] = ia;
2606                          pixelData[1] = iv;
2607                          pixelData[2] = iv;
2608                          pixelData[3] = iv;
2609                        }
2610                      else
2611                        {
2612                          pixelData[0] = iv;
2613                          pixelData[1] = iv;
2614                          pixelData[2] = iv;
2615                          pixelData[3] = ia;
2616                        }
2617                    }
2618                  else
2619                    {
2620                      // Scale from [0.0 ... 1.0]
2621                      iv = fv * scale;
2622                      pixelData[0] = iv;
2623                      pixelData[1] = iv;
2624                      pixelData[2] = iv;
2625                    }
2626
2627                  //[new setPixel: pixelData atX: x y: y];
2628                  setP(new, setPSel, pixelData, x, y);
2629                }
2630            }
2631        }
2632      else
2633        {
2634          SEL getCSel = @selector(colorAtX:y:);
2635          SEL setCSel = @selector(setColor:atX:y:);
2636          IMP getC = [self methodForSelector: getCSel];
2637          IMP setC = [new methodForSelector: setCSel];
2638          NSInteger i, j;
2639
2640          NSDebugLLog(@"NSImage", @"Slow converting %@ bitmap data to %@",
2641                      _colorSpace, colorSpaceName);
2642          for (j = 0; j < _pixelsHigh; j++)
2643            {
2644              CREATE_AUTORELEASE_POOL(pool);
2645
2646              for (i = 0; i < _pixelsWide; i++)
2647                {
2648                  NSColor *c;
2649
2650                  //c = [self colorAtX: i y: j];
2651                  c = getC(self, getCSel, i, j);
2652                  //[new setColor: c atX: i y: j];
2653                  setC(new, setCSel, c, i, j);
2654                }
2655              [pool drain];
2656            }
2657        }
2658
2659      return AUTORELEASE(new);
2660    }
2661}
2662
2663@end
2664