libs-gui/Source/NSBitmapImageRep.m
fedor fc402789db Include <> -> ""
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@16917 72102866-910b-0410-8b05-ffd578937521
2003-06-13 15:01:12 +00:00

750 lines
17 KiB
Objective-C

/** <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 "gnustep/gui/config.h"
#include <stdlib.h>
#include <math.h>
#include <tiff.h>
#include <Foundation/NSException.h>
#include <Foundation/NSArray.h>
#include <Foundation/NSData.h>
#include <Foundation/NSDebug.h>
#include "AppKit/NSGraphics.h"
#include "AppKit/NSView.h"
#include "AppKit/NSPasteboard.h"
#include "AppKit/NSBitmapImageRep.h"
#include "AppKit/AppKitExceptions.h"
#include "gnustep/gui/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;
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;
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;
image = NSTiffOpenDataRead((char *)[tiffData bytes], [tiffData length]);
if (image == 0)
{
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 */
+ (NSArray*) imageRepsWithFile: (NSString *)filename
{
NSString *ext;
int images;
NSMutableArray *array;
NSBitmapImageRep* imageRep;
/* Don't use this for TIFF images, use the regular ...Data methods */
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);
return array;
}
/* 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]))
{
RELEASE(self);
NSLog(@"Tiff read invalid TIFF image data in directory %d", imageNumber);
return nil;
}
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