libs-gui/Source/NSBitmapImageRep.m

784 lines
18 KiB
Mathematica
Raw Normal View History

/** <title>NSBitmapImageRep.m</title>
<abstract>Bitmap image representation.</abstract>
Copyright (C) 1996 Free Software Foundation, Inc.
Author: Adam Fedor <fedor@gnu.org>
Date: Feb 1996
This file is part of the GNUstep GUI Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; see the file COPYING.LIB.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <stdlib.h>
#include <math.h>
#include <tiff.h>
#include <Foundation/NSArray.h>
#include <Foundation/NSData.h>
#include <Foundation/NSDebug.h>
#include <Foundation/NSException.h>
#include <Foundation/NSFileManager.h>
#include "AppKit/AppKitExceptions.h"
#include "AppKit/NSBitmapImageRep.h"
#include "AppKit/NSGraphics.h"
#include "AppKit/NSPasteboard.h"
#include "AppKit/NSView.h"
#include "nsimage-tiff.h"
/* Maximum number of planes */
#define MAX_PLANES 5
/* Backend methods (optional) */
@interface NSBitmapImageRep (Backend)
+ (NSArray *) _wrasterFileTypes;
- _initFromWrasterFile: (NSString *)filename number: (int)imageNumber;
// GNUstep extension
- _initFromTIFFImage: (TIFF *)image number: (int)imageNumber;
@end
@implementation NSBitmapImageRep
+ (BOOL) canInitWithData: (NSData *)data
{
TIFF *image = NULL;
if (data == nil)
{
return NO;
}
image = NSTiffOpenDataRead ((char *)[data bytes], [data length]);
if (image != NULL)
{
NSTiffClose (image);
return YES;
}
else
{
return NO;
}
}
+ (NSArray *) imageUnfilteredFileTypes
{
static NSArray *types = nil;
if (types == nil)
{
NSArray *wtypes = [self _wrasterFileTypes];
if (wtypes != nil)
{
types = [wtypes mutableCopy];
[(NSMutableArray *)types insertObject: @"tiff" atIndex: 0];
[(NSMutableArray *)types insertObject: @"tif" atIndex: 1];
}
else
{
types = [[NSArray alloc] initWithObjects: @"tiff", @"tif", nil];
}
}
return types;
}
+ (NSArray *) imageUnfilteredPasteboardTypes
{
static NSArray *types = nil;
if (types == nil)
{
types = [[NSArray alloc] initWithObjects: NSTIFFPboardType, nil];
}
return types;
}
+ (id) imageRepWithData: (NSData *)tiffData
{
NSArray* array;
array = [self imageRepsWithData: tiffData];
if ([array count])
{
return [array objectAtIndex: 0];
}
return nil;
}
+ (NSArray*) imageRepsWithData: (NSData *)tiffData
{
int i, images;
TIFF *image;
NSMutableArray *array;
if (tiffData == nil)
{
return nil;
}
image = NSTiffOpenDataRead((char *)[tiffData bytes], [tiffData length]);
if (image == NULL)
{
NSLog(@"Tiff unable to open/parse TIFF data");
return nil;
}
images = NSTiffGetImageCount(image);
NSDebugLLog(@"NSImage", @"Image contains %d directories", images);
array = [NSMutableArray arrayWithCapacity: images];
for (i = 0; i < images; i++)
{
NSBitmapImageRep* imageRep;
imageRep = [[self alloc] _initFromTIFFImage: image number: i];
if (imageRep)
{
[array addObject: AUTORELEASE(imageRep)];
}
}
NSTiffClose(image);
return array;
}
/* Loads only the default (first) image from the TIFF image contained in
data. */
- (id) initWithData: (NSData *)tiffData
{
TIFF *image;
if (tiffData == nil)
{
RELEASE(self);
return nil;
}
image = NSTiffOpenDataRead((char *)[tiffData bytes], [tiffData length]);
if (image == NULL)
{
RELEASE(self);
NSLog(@"Tiff read invalid TIFF info from data");
return nil;
}
[self _initFromTIFFImage:image number: -1];
NSTiffClose(image);
return self;
}
- (id) initWithFocusedViewRect: (NSRect)rect
{
// TODO
[self notImplemented: _cmd];
RELEASE(self);
return nil;
}
/* This is the designated initializer */
/* Note: If data is actaully passed to us in planes, we DO NOT own this
data and we DO NOT copy it. Just assume that it will always be available.
*/
- (id) initWithBitmapDataPlanes: (unsigned char **)planes
pixelsWide: (int)width
pixelsHigh: (int)height
bitsPerSample: (int)bps
samplesPerPixel: (int)spp
hasAlpha: (BOOL)alpha
isPlanar: (BOOL)isPlanar
colorSpaceName: (NSString *)colorSpaceName
bytesPerRow: (int)rowBytes
bitsPerPixel: (int)pixelBits
{
if (!bps || !spp || !width || !height)
{
[NSException raise: NSInvalidArgumentException
format: @"Required arguments not specified creating NSBitmapImageRep"];
}
_pixelsWide = width;
_pixelsHigh = height;
_size.width = width;
_size.height = height;
_bitsPerSample = bps;
_numColors = spp;
_hasAlpha = alpha;
_isPlanar = isPlanar;
_colorSpace = RETAIN(colorSpaceName);
if (!pixelBits)
pixelBits = bps * ((_isPlanar) ? 1 : spp);
_bitsPerPixel = pixelBits;
if (!rowBytes)
rowBytes = ceil((float)width * _bitsPerPixel / 8);
_bytesPerRow = rowBytes;
_imagePlanes = NSZoneMalloc([self zone], sizeof(unsigned char*) * MAX_PLANES);
if (planes)
{
unsigned int i;
for (i = 0; i < MAX_PLANES; i++)
_imagePlanes[i] = NULL;
for (i = 0; i < ((_isPlanar) ? _numColors : 1); i++)
_imagePlanes[i] = planes[i];
}
else
{
unsigned char* bits;
long length;
unsigned int i;
// No image data was given, allocate it.
length = (long)((_isPlanar) ? _numColors : 1) * _bytesPerRow *
_pixelsHigh * sizeof(unsigned char);
_imageData = [[NSMutableData alloc] initWithLength: length];
bits = [_imageData mutableBytes];
_imagePlanes[0] = bits;
if (_isPlanar)
{
for (i=1; i < _numColors; i++)
_imagePlanes[i] = bits + i*_bytesPerRow * _pixelsHigh;
for (i= _numColors; i < MAX_PLANES; i++)
_imagePlanes[i] = NULL;
}
else
{
for (i= 1; i < MAX_PLANES; i++)
_imagePlanes[i] = NULL;
}
}
if (alpha)
{
unsigned char *bData = (unsigned char*)[self bitmapData];
BOOL allOpaque = YES;
unsigned offset = _numColors - 1;
unsigned limit = _size.height * _size.width;
unsigned i;
for (i = 0; i < limit; i++)
{
unsigned a;
bData += offset;
a = *bData++;
if (a != 255)
{
allOpaque = NO;
break;
}
}
[self setOpaque: allOpaque];
}
else
{
[self setOpaque: YES];
}
return self;
}
- (void)colorizeByMappingGray:(float)midPoint
toColor:(NSColor *)midPointColor
blackMapping:(NSColor *)shadowColor
whiteMapping:(NSColor *)lightColor
{
// TODO
}
- (id)initWithBitmapHandle:(void *)bitmap
{
// TODO Only needed on MS Windows
RELEASE(self);
return nil;
}
- (id)initWithIconHandle:(void *)icon
{
// TODO Only needed on MS Windows
RELEASE(self);
return nil;
}
- (void) dealloc
{
NSZoneFree([self zone],_imagePlanes);
RELEASE(_imageData);
[super dealloc];
}
//
// Getting Information about the Image
//
- (int) bitsPerPixel
{
return _bitsPerPixel;
}
- (int) samplesPerPixel
{
return _numColors;
}
- (BOOL) isPlanar
{
return _isPlanar;
}
- (int) numberOfPlanes
{
return (_isPlanar) ? _numColors : 1;
}
- (int) bytesPerPlane
{
return _bytesPerRow*_pixelsHigh;
}
- (int) bytesPerRow
{
return _bytesPerRow;
}
//
// Getting Image Data
//
- (unsigned char *) bitmapData
{
unsigned char *planes[MAX_PLANES];
[self getBitmapDataPlanes: planes];
return planes[0];
}
- (void) getBitmapDataPlanes: (unsigned char **)data
{
unsigned int i;
if (data)
{
for (i = 0; i < _numColors; i++)
{
data[i] = _imagePlanes[i];
}
}
}
- (BOOL) draw
{
NSRect irect = NSMakeRect(0, 0, _size.width, _size.height);
NSDrawBitmap(irect,
_pixelsWide,
_pixelsHigh,
_bitsPerSample,
_numColors,
_bitsPerPixel,
_bytesPerRow,
_isPlanar,
_hasAlpha,
_colorSpace,
(const unsigned char **)_imagePlanes);
return YES;
}
//
// Producing a TIFF Representation of the Image
//
+ (NSData*) TIFFRepresentationOfImageRepsInArray: (NSArray *)anArray
{
//FIXME: This only outputs one of the ImageReps
NSEnumerator *enumerator = [anArray objectEnumerator];
NSImageRep *rep;
while ((rep = [enumerator nextObject]) != nil)
{
if ([rep isKindOfClass: self])
{
return [(NSBitmapImageRep*)rep TIFFRepresentation];
}
}
return nil;
}
+ (NSData*) TIFFRepresentationOfImageRepsInArray: (NSArray *)anArray
usingCompression: (NSTIFFCompression)type
factor: (float)factor
{
//FIXME: This only outputs one of the ImageReps
NSEnumerator *enumerator = [anArray objectEnumerator];
NSImageRep *rep;
while ((rep = [enumerator nextObject]) != nil)
{
if ([rep isKindOfClass: self])
{
return [(NSBitmapImageRep*)rep TIFFRepresentationUsingCompression: type
factor: factor];
}
}
return nil;
}
- (NSData*) TIFFRepresentation
{
return [self TIFFRepresentationUsingCompression: _compression
factor: _comp_factor];
}
- (NSData*) TIFFRepresentationUsingCompression: (NSTIFFCompression)type
factor: (float)factor
{
NSTiffInfo info;
TIFF *image;
char *bytes = 0;
long length = 0;
info.imageNumber = 0;
info.subfileType = 255;
info.width = _pixelsWide;
info.height = _pixelsHigh;
info.bitsPerSample = _bitsPerSample;
info.samplesPerPixel = _numColors;
if (_isPlanar)
info.planarConfig = PLANARCONFIG_SEPARATE;
else
info.planarConfig = PLANARCONFIG_CONTIG;
if (_colorSpace == NSDeviceRGBColorSpace)
info.photoInterp = PHOTOMETRIC_RGB;
else if (_colorSpace == NSDeviceWhiteColorSpace)
info.photoInterp = PHOTOMETRIC_MINISBLACK;
else if (_colorSpace == NSDeviceBlackColorSpace)
info.photoInterp = PHOTOMETRIC_MINISWHITE;
else
info.photoInterp = PHOTOMETRIC_RGB;
info.extraSamples = (_hasAlpha) ? 1 : 0;
if (type == 0)
type = COMPRESSION_NONE;
info.compression = type;
info.quality = (1 - ((float)factor)/255.0) * 100;
info.numImages = 1;
info.error = 0;
image = NSTiffOpenDataWrite(&bytes, &length);
if (image == 0)
{
[NSException raise: NSTIFFException format: @"Write TIFF open failed"];
}
if (NSTiffWrite(image, &info, [self bitmapData]) != 0)
{
[NSException raise: NSTIFFException format: @"Write TIFF data failed"];
}
NSTiffClose(image);
return [NSData dataWithBytesNoCopy: bytes length: length];
}
+ (NSData *)representationOfImageRepsInArray:(NSArray *)imageReps
usingType:(NSBitmapImageFileType)storageType
properties:(NSDictionary *)properties
{
// TODO
return nil;
}
- (NSData *)representationUsingType:(NSBitmapImageFileType)storageType
properties:(NSDictionary *)properties
{
// TODO
return nil;
}
//
// Setting and Checking Compression Types
//
+ (void) getTIFFCompressionTypes: (const NSTIFFCompression **)list
count: (int *)numTypes
{
static NSTIFFCompression types[] = {
NSTIFFCompressionNone,
NSTIFFCompressionCCITTFAX3,
NSTIFFCompressionCCITTFAX4,
NSTIFFCompressionLZW,
NSTIFFCompressionJPEG,
NSTIFFCompressionPackBits
};
*list = types;
*numTypes = sizeof(types)/sizeof(*types);
}
+ (NSString*) localizedNameForTIFFCompressionType: (NSTIFFCompression)type
{
switch (type)
{
case NSTIFFCompressionNone: return @"NSTIFFCompressionNone";
case NSTIFFCompressionCCITTFAX3: return @"NSTIFFCompressionCCITTFAX3";
case NSTIFFCompressionCCITTFAX4: return @"NSTIFFCompressionCCITTFAX4";
case NSTIFFCompressionLZW: return @"NSTIFFCompressionLZW";
case NSTIFFCompressionJPEG: return @"NSTIFFCompressionJPEG";
case NSTIFFCompressionNEXT: return @"NSTIFFCompressionNEXT";
case NSTIFFCompressionPackBits: return @"NSTIFFCompressionPackBits";
case NSTIFFCompressionOldJPEG: return @"NSTIFFCompressionOldJPEG";
default: return nil;
}
}
- (BOOL) canBeCompressedUsing: (NSTIFFCompression)compression
{
switch (compression)
{
case NSTIFFCompressionCCITTFAX3:
case NSTIFFCompressionCCITTFAX4:
if (_numColors == 1 && _bitsPerSample == 1)
return YES;
else
return NO;
case NSTIFFCompressionNone:
case NSTIFFCompressionLZW:
case NSTIFFCompressionJPEG:
case NSTIFFCompressionPackBits:
return YES;
case NSTIFFCompressionNEXT:
case NSTIFFCompressionOldJPEG:
default:
return NO;
}
}
- (void) getCompression: (NSTIFFCompression*)compression
factor: (float*)factor
{
*compression = _compression;
*factor = _comp_factor;
}
- (void) setCompression: (NSTIFFCompression)compression
factor: (float)factor
{
_compression = compression;
_comp_factor = factor;
}
- (void)setProperty:(NSString *)property withValue:(id)value
{
// TODO
}
- (id)valueForProperty:(NSString *)property
{
// TODO
return nil;
}
// NSCopying protocol
- (id) copyWithZone: (NSZone *)zone
{
NSBitmapImageRep *copy;
copy = (NSBitmapImageRep*)[super copyWithZone: zone];
copy->_imageData = [_imageData copyWithZone: zone];
copy->_imagePlanes = NSZoneMalloc(zone, sizeof(unsigned char*) * MAX_PLANES);
if (_imageData == nil)
{
memcpy(copy->_imagePlanes, _imagePlanes, sizeof(unsigned char*) * MAX_PLANES);
}
else
{
unsigned char* bits;
unsigned int i;
bits = [copy->_imageData mutableBytes];
copy->_imagePlanes[0] = bits;
if (_isPlanar)
{
for (i=1; i < _numColors; i++)
copy->_imagePlanes[i] = bits + i*_bytesPerRow * _pixelsHigh;
for (i= _numColors; i < MAX_PLANES; i++)
copy->_imagePlanes[i] = NULL;
}
else
{
for (i= 1; i < MAX_PLANES; i++)
copy->_imagePlanes[i] = NULL;
}
}
return copy;
}
//
// NSCoding protocol
//
- (void) encodeWithCoder: (NSCoder*)aCoder
{
NSData *data = [self TIFFRepresentation];
[super encodeWithCoder: aCoder];
[aCoder encodeObject: data];
}
- (id) initWithCoder: (NSCoder*)aDecoder
{
NSData *data;
self = [super initWithCoder: aDecoder];
data = [aDecoder decodeObject];
return [self initWithData: data];
}
@end
@implementation NSBitmapImageRep (GNUstepExtension)
/* A special method used mostly when we have the wraster library in the
backend, which can read several more image formats.
Don't use this for TIFF images, use the regular ...Data methods */
+ (NSArray*) imageRepsWithFile: (NSString *)filename
{
NSString *ext;
int images;
NSMutableArray *array;
NSBitmapImageRep* imageRep;
NSFileManager *manager = [NSFileManager defaultManager];
if ([manager fileExistsAtPath: filename] == NO)
{
return nil;
}
ext = [filename pathExtension];
if (!ext)
{
NSLog(@"Extension missing from filename - '%@'", filename);
return nil;
}
/* This is a quick hack to make it work - if the extension is that
of a tiff file, we init with data */
if ([ext isEqualToString: @"tiff"] || [ext isEqualToString: @"tif"])
{
NSData* data = [NSData dataWithContentsOfFile: filename];
return [self imageRepsWithData: data];
}
/* Otherwise, we attempt using wraster classes to load the file
(which could be png, etc) */
array = [NSMutableArray arrayWithCapacity: 2];
images = 0;
do
{
imageRep = [[[self class] alloc] _initFromWrasterFile: filename
number: images];
if (imageRep)
{
[array addObject: AUTORELEASE(imageRep)];
}
images++;
}
while (imageRep);
if ([array count] > 0)
{
return array;
}
else
{
return nil;
}
}
/* Given a TIFF image (from the libtiff library), load the image information
into our data structure. Reads the specified image. */
- _initFromTIFFImage: (TIFF *)image number: (int)imageNumber
{
NSString* space;
NSTiffInfo* info;
/* Seek to the correct image and get the dictionary information */
info = NSTiffGetInfo(imageNumber, image);
if (!info)
{
RELEASE(self);
NSLog(@"Tiff read invalid TIFF info in directory %d", imageNumber);
return nil;
}
/* 8-bit RGB will be converted to 24-bit by the tiff routines, so account
for this. */
space = nil;
switch(info->photoInterp)
{
case PHOTOMETRIC_MINISBLACK: space = NSDeviceWhiteColorSpace; break;
case PHOTOMETRIC_MINISWHITE: space = NSDeviceBlackColorSpace; break;
case PHOTOMETRIC_RGB: space = NSDeviceRGBColorSpace; break;
case PHOTOMETRIC_PALETTE:
space = NSDeviceRGBColorSpace;
info->samplesPerPixel = 3;
break;
default:
break;
}
[self initWithBitmapDataPlanes: NULL
pixelsWide: info->width
pixelsHigh: info->height
bitsPerSample: info->bitsPerSample
samplesPerPixel: info->samplesPerPixel
hasAlpha: (info->extraSamples > 0)
isPlanar: (info->planarConfig == PLANARCONFIG_SEPARATE)
colorSpaceName: space
bytesPerRow: 0
bitsPerPixel: 0];
_compression = info->compression;
_comp_factor = 255 * (1 - ((float)info->quality)/100.0);
if (NSTiffRead(image, info, [self bitmapData]))
{
OBJC_FREE(info);
RELEASE(self);
NSLog(@"Tiff read invalid TIFF image data in directory %d", imageNumber);
return nil;
}
OBJC_FREE(info);
return self;
}
// This are just placeholders for the implementation in the backend
- _initFromWrasterFile: (NSString *)filename number: (int)imageNumber
{
return nil;
}
+ (NSArray *) _wrasterFileTypes
{
return nil;
}
@end