mirror of
https://github.com/darlinghq/darling-cocotron.git
synced 2024-10-07 09:33:28 +00:00
405 lines
11 KiB
Objective-C
405 lines
11 KiB
Objective-C
#import <Onyx2D/O2Encoder_TIFF.h>
|
|
#import <Onyx2D/O2Decoder_TIFF.h>
|
|
|
|
O2TIFFEncoderRef O2TIFFEncoderCreate(O2DataConsumerRef consumer) {
|
|
O2TIFFEncoderRef self=NSZoneCalloc(NULL,1,sizeof(struct O2TIFFEncoder));
|
|
self->_bigEndian=YES;
|
|
self->_consumerOffset=0;
|
|
self->_consumerPosition=0;
|
|
self->_bufferCapacity=0;
|
|
self->_bufferCount=0;
|
|
self->_mutableBytes=NULL;
|
|
self->_consumer=(id)CFRetain(consumer);
|
|
return self;
|
|
}
|
|
|
|
void O2TIFFEncoderDealloc(O2TIFFEncoderRef self) {
|
|
if(self->_mutableBytes!=NULL)
|
|
NSZoneFree(NULL,self->_mutableBytes);
|
|
if(self->_consumer!=NULL)
|
|
CFRelease(self->_consumer);
|
|
NSZoneFree(NULL,self);
|
|
}
|
|
|
|
static void beginBuffering(O2TIFFEncoderRef self){
|
|
self->_bufferCapacity=256;
|
|
self->_bufferCount=0;
|
|
self->_mutableBytes=NSZoneMalloc(NULL,self->_bufferCapacity);
|
|
}
|
|
|
|
static void endBuffering(O2TIFFEncoderRef self){
|
|
O2DataConsumerPutBytes(self->_consumer,self->_mutableBytes,self->_bufferCount);
|
|
|
|
NSZoneFree(NULL,self->_mutableBytes);
|
|
self->_mutableBytes=NULL;
|
|
self->_consumerOffset+=self->_bufferCount;
|
|
}
|
|
|
|
static uint32_t currentPosition(O2TIFFEncoderRef self){
|
|
return self->_consumerPosition;
|
|
}
|
|
|
|
static uint32_t setPosition(O2TIFFEncoderRef self,uint32_t value){
|
|
uint32_t result=self->_consumerPosition;
|
|
|
|
self->_consumerPosition=value;
|
|
self->_bufferCount=value-self->_consumerOffset;
|
|
|
|
return result;
|
|
}
|
|
|
|
static void ensureByteCount(O2TIFFEncoderRef self,size_t count){
|
|
if(self->_bufferCount+count>self->_bufferCapacity){
|
|
while(self->_bufferCount+count>self->_bufferCapacity)
|
|
self->_bufferCapacity*=2;
|
|
|
|
self->_mutableBytes=NSZoneRealloc(NULL,self->_mutableBytes,self->_bufferCapacity);
|
|
}
|
|
}
|
|
|
|
static void putBytes(O2TIFFEncoderRef self,uint8_t *values,uint32_t count){
|
|
uint32_t i;
|
|
|
|
ensureByteCount(self,count);
|
|
|
|
for(i=0;i<count;i++,self->_bufferCount++,self->_consumerPosition++)
|
|
self->_mutableBytes[self->_bufferCount]=values[i];
|
|
|
|
}
|
|
|
|
static void putUnsigned8(O2TIFFEncoderRef self,uint8_t value){
|
|
putBytes(self,&value,1);
|
|
}
|
|
|
|
static void putUnsigned16(O2TIFFEncoderRef self,uint16_t value){
|
|
uint8_t bytes[2];
|
|
|
|
if(self->_bigEndian){
|
|
bytes[0]=value>>8;
|
|
bytes[1]=value&0xFF;
|
|
}
|
|
else {
|
|
bytes[1]=value>>8;
|
|
bytes[0]=value&0xFF;
|
|
}
|
|
|
|
putBytes(self,bytes,2);
|
|
}
|
|
|
|
static void putUnsigned32(O2TIFFEncoderRef self,uint32_t value){
|
|
uint8_t bytes[4];
|
|
|
|
if(self->_bigEndian){
|
|
bytes[0]=value>>24;
|
|
bytes[1]=value>>16;
|
|
bytes[2]=value>>8;
|
|
bytes[3]=value&0xFF;
|
|
}
|
|
else {
|
|
bytes[3]=(value>>24)&0xFF;
|
|
bytes[2]=(value>>16)&0xFF;
|
|
bytes[1]=(value>>8);
|
|
bytes[0]=value&0xFF;
|
|
}
|
|
|
|
putBytes(self,bytes,4);
|
|
}
|
|
|
|
static void putUnsigned32AtPosition(O2TIFFEncoderRef self,uint32_t value,uint32_t position){
|
|
uint32_t save=setPosition(self,position);
|
|
putUnsigned32(self,value);
|
|
setPosition(self,save);
|
|
}
|
|
|
|
|
|
static void encodeUnsigned16(O2TIFFEncoderRef self,uint16_t value){
|
|
putUnsigned16(self,NSTIFFTypeSHORT);
|
|
putUnsigned32(self,1);
|
|
putUnsigned16(self,value);
|
|
putUnsigned16(self,0); // pad
|
|
}
|
|
|
|
static void encodeUnsigned32(O2TIFFEncoderRef self,uint32_t value){
|
|
putUnsigned16(self,NSTIFFTypeLONG);
|
|
putUnsigned32(self,1);
|
|
putUnsigned32(self,value);
|
|
}
|
|
|
|
static uint32_t reserveRational(O2TIFFEncoderRef self){
|
|
uint32_t result=currentPosition(self);
|
|
putUnsigned16(self,NSTIFFTypeRATIONAL);
|
|
putUnsigned32(self,1);
|
|
putUnsigned32(self,0);
|
|
return result;
|
|
}
|
|
|
|
static void encodeRationalAtPosition(O2TIFFEncoderRef self,double value,uint32_t position){
|
|
uint32_t save=setPosition(self,position);
|
|
|
|
putUnsigned16(self,NSTIFFTypeRATIONAL);
|
|
putUnsigned32(self,1);
|
|
putUnsigned32(self,save);
|
|
setPosition(self,save);
|
|
|
|
uint32_t denominator=1000000;
|
|
|
|
value*=denominator;
|
|
putUnsigned32(self,value);
|
|
putUnsigned32(self,denominator);
|
|
}
|
|
|
|
static void encodeUnsigned16OrUnsigned32(O2TIFFEncoderRef self,uint32_t value){
|
|
if((value&0xFFFF)==0)
|
|
encodeUnsigned16(self,value);
|
|
else
|
|
encodeUnsigned32(self,value);
|
|
}
|
|
|
|
#if 0
|
|
static void encodeUnsigned32AtPosition(O2TIFFEncoderRef self,uint32_t value,uint32_t position){
|
|
uint32_t save=setPosition(self,position);
|
|
|
|
putUnsigned16(self,NSTIFFTypeLONG);
|
|
putUnsigned32(self,value);
|
|
|
|
setPosition(self,save);
|
|
}
|
|
|
|
|
|
static void encodeArrayOfUnsigned8(O2TIFFEncoderRef self,uint8_t *values,uint32_t count){
|
|
putUnsigned32(self,count);
|
|
if(count==1)
|
|
putUnsigned8(self,values[0]);
|
|
else if(count==2){
|
|
putUnsigned8(self,values[0]);
|
|
putUnsigned8(self,values[1]);
|
|
}
|
|
else {
|
|
uint32_t offset=self->_consumerPosition+4;
|
|
|
|
putUnsigned32(self,offset);
|
|
|
|
uint32_t i;
|
|
for(i=0;i<count;i++)
|
|
putUnsigned32(self,values[i]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static uint32_t reserveArrayOfUnsigned16(O2TIFFEncoderRef self){
|
|
uint32_t result=currentPosition(self);
|
|
|
|
putUnsigned16(self,NSTIFFTypeSHORT);
|
|
putUnsigned32(self,0);
|
|
putUnsigned32(self,0);
|
|
|
|
return result;
|
|
}
|
|
|
|
static void encodeArrayOfUnsigned16AtPosition(O2TIFFEncoderRef self,uint16_t *values,size_t count,uint32_t position){
|
|
uint32_t save=setPosition(self,position);
|
|
|
|
putUnsigned16(self,NSTIFFTypeSHORT);
|
|
putUnsigned32(self,count);
|
|
|
|
if(count==1){
|
|
putUnsigned16(self,values[0]);
|
|
setPosition(self,save);
|
|
}
|
|
else if(count==2){
|
|
putUnsigned16(self,values[0]);
|
|
putUnsigned16(self,values[1]);
|
|
setPosition(self,save);
|
|
}
|
|
else {
|
|
int i;
|
|
|
|
putUnsigned32(self,save);
|
|
setPosition(self,save);
|
|
|
|
for(i=0;i<count;i++)
|
|
putUnsigned16(self,values[i]);
|
|
}
|
|
}
|
|
|
|
static size_t reserveArrayOfUnsigned32(O2TIFFEncoderRef self){
|
|
size_t result=currentPosition(self);
|
|
|
|
putUnsigned16(self,NSTIFFTypeLONG);
|
|
putUnsigned32(self,0);
|
|
putUnsigned32(self,0);
|
|
|
|
return result;
|
|
}
|
|
|
|
static void encodeArrayOfUnsigned32AtPosition(O2TIFFEncoderRef self,uint32_t *values,size_t count,uint32_t position){
|
|
uint32_t save=setPosition(self,position);
|
|
|
|
putUnsigned16(self,NSTIFFTypeLONG);
|
|
putUnsigned32(self,count);
|
|
if(count==1){
|
|
putUnsigned32(self,values[0]);
|
|
setPosition(self,save);
|
|
}
|
|
else {
|
|
int i;
|
|
|
|
putUnsigned32(self,save);
|
|
setPosition(self,save);
|
|
|
|
for(i=0;i<count;i++)
|
|
putUnsigned32(self,values[i]);
|
|
}
|
|
|
|
}
|
|
|
|
void O2TIFFEncoderBegin(O2TIFFEncoderRef self) {
|
|
beginBuffering(self);
|
|
if(self->_bigEndian){
|
|
putUnsigned8(self,'M');
|
|
putUnsigned8(self,'M');
|
|
}
|
|
else {
|
|
putUnsigned8(self,'I');
|
|
putUnsigned8(self,'I');
|
|
}
|
|
putUnsigned16(self,42);
|
|
endBuffering(self);
|
|
}
|
|
|
|
void pack_argb8u_as_rgb8u(O2argb8u *imageRow,size_t width,uint8_t *tiffRow){
|
|
int i,byteIndex=0;
|
|
|
|
for(i=0;i<width;i++){
|
|
O2argb8u pixel=imageRow[i];
|
|
tiffRow[byteIndex++]=pixel.r;
|
|
tiffRow[byteIndex++]=pixel.g;
|
|
tiffRow[byteIndex++]=pixel.b;
|
|
}
|
|
}
|
|
|
|
void pack_argb8u_as_rgba8u(O2argb8u *imageRow,size_t width,uint8_t *tiffRow){
|
|
int i,byteIndex=0;
|
|
|
|
for(i=0;i<width;i++){
|
|
O2argb8u pixel=imageRow[i];
|
|
tiffRow[byteIndex++]=pixel.r;
|
|
tiffRow[byteIndex++]=pixel.g;
|
|
tiffRow[byteIndex++]=pixel.b;
|
|
tiffRow[byteIndex++]=pixel.a;
|
|
}
|
|
}
|
|
|
|
|
|
void O2TIFFEncoderWriteImage(O2TIFFEncoderRef self,O2ImageRef image,CFDictionaryRef properties,bool lastImage) {
|
|
size_t imageWidth=O2ImageGetWidth(image);
|
|
size_t imageHeight=O2ImageGetHeight(image);
|
|
O2ImageAlphaInfo imageAlphaInfo=O2ImageGetAlphaInfo(image);
|
|
// FIX, needs to premultiply
|
|
bool imageHasAlpha=(imageAlphaInfo==kO2ImageAlphaPremultipliedLast ||
|
|
imageAlphaInfo==kO2ImageAlphaPremultipliedFirst ||
|
|
imageAlphaInfo==kO2ImageAlphaLast ||
|
|
imageAlphaInfo==kO2ImageAlphaFirst);
|
|
|
|
int tiffBitsPerComponent=8;
|
|
int tiffSamplesPerPixel=imageHasAlpha?4:3;
|
|
int tiffBitsPerPixel=tiffBitsPerComponent*tiffSamplesPerPixel;
|
|
int tiffBytesPerRow=(tiffBitsPerPixel*imageWidth)/8;
|
|
|
|
size_t tiffPixelByteCount=imageHeight*tiffBytesPerRow;
|
|
|
|
uint32_t idealStripSize=MAX(8192,tiffBytesPerRow); // 8k recommended by spec.
|
|
uint32_t rowsPerStrip=(idealStripSize/tiffBytesPerRow);
|
|
uint32_t stripSize=rowsPerStrip*tiffBytesPerRow;
|
|
uint32_t stripCount=(tiffPixelByteCount+(stripSize-1))/stripSize;
|
|
uint32_t stripOffsetsPosition;
|
|
uint32_t stripOffsets[stripCount];
|
|
uint32_t stripByteCountsPosition;
|
|
uint32_t stripByteCounts[stripCount];
|
|
O2argb8u imageRowBuffer[imageWidth];
|
|
O2argb8u *imageRow;
|
|
uint8_t tiffRowBuffer[tiffBytesPerRow];
|
|
|
|
int strip,y=0;
|
|
|
|
for(strip=0;strip<stripCount;strip++)
|
|
stripOffsets[strip]=stripByteCounts[strip]=0;
|
|
|
|
beginBuffering(self);
|
|
|
|
putUnsigned32(self,currentPosition(self)+4);
|
|
|
|
putUnsigned16(self,imageHasAlpha?13:12);
|
|
putUnsigned16(self,NSTIFFTagImageWidth);
|
|
encodeUnsigned16OrUnsigned32(self,imageWidth);
|
|
putUnsigned16(self,NSTIFFTagImageLength);
|
|
encodeUnsigned16OrUnsigned32(self,imageHeight);
|
|
putUnsigned16(self,NSTIFFTagBitsPerSample);
|
|
uint32_t bpsPosition=reserveArrayOfUnsigned16(self);
|
|
putUnsigned16(self,NSTIFFTagCompression);
|
|
encodeUnsigned16(self,NSTIFFCompression_none);
|
|
if(imageHasAlpha){
|
|
putUnsigned16(self,NSTIFFTagExtraSamples);
|
|
encodeUnsigned16(self,NSTIFFExtraSamples_associatedAlpha);
|
|
}
|
|
putUnsigned16(self,NSTIFFTagPhotometricInterpretation);
|
|
encodeUnsigned16(self,NSTIFFPhotometricInterpretationRGB);
|
|
putUnsigned16(self,NSTIFFTagStripOffsets);
|
|
stripOffsetsPosition=reserveArrayOfUnsigned32(self);
|
|
putUnsigned16(self,NSTIFFTagSamplesPerPixel);
|
|
encodeUnsigned16(self,tiffSamplesPerPixel);
|
|
putUnsigned16(self,NSTIFFTagRowsPerStrip);
|
|
encodeUnsigned32(self,rowsPerStrip);
|
|
putUnsigned16(self,NSTIFFTagStripByteCounts);
|
|
stripByteCountsPosition=reserveArrayOfUnsigned32(self);
|
|
putUnsigned16(self,NSTIFFTagXResolution);
|
|
uint32_t xResolutionPosition=reserveRational(self);
|
|
putUnsigned16(self,NSTIFFTagYResolution);
|
|
uint32_t yResolutionPosition=reserveRational(self);
|
|
putUnsigned16(self,NSTIFFTagResolutionUnit);
|
|
encodeUnsigned16(self,NSTIFFResolutionUnit_inch);
|
|
|
|
uint32_t nextEntryOffsetPosition=currentPosition(self);
|
|
putUnsigned32(self,0);
|
|
|
|
for(strip=0;strip<stripCount;strip++){
|
|
int row,rowCount=MIN(rowsPerStrip,imageHeight-(strip*rowsPerStrip));
|
|
|
|
stripOffsets[strip]=currentPosition(self);
|
|
|
|
uint32_t compressedSize=rowCount*tiffBytesPerRow;
|
|
|
|
for(row=0;row<rowCount;row++,y++){
|
|
|
|
imageRow=image->_read_argb8u(image,0,y,imageRowBuffer,imageWidth);
|
|
if(imageRow==NULL)
|
|
imageRow=imageRowBuffer;
|
|
|
|
if(imageHasAlpha)
|
|
pack_argb8u_as_rgba8u(imageRow,imageWidth,tiffRowBuffer);
|
|
else
|
|
pack_argb8u_as_rgb8u(imageRow,imageWidth,tiffRowBuffer);
|
|
|
|
putBytes(self,tiffRowBuffer,tiffBytesPerRow);
|
|
};
|
|
|
|
stripByteCounts[strip]=compressedSize;
|
|
}
|
|
|
|
encodeArrayOfUnsigned32AtPosition(self,stripOffsets,stripCount,stripOffsetsPosition);
|
|
encodeArrayOfUnsigned32AtPosition(self,stripByteCounts,stripCount,stripByteCountsPosition);
|
|
uint16_t bps[4]={8,8,8,8};
|
|
encodeArrayOfUnsigned16AtPosition(self,bps,tiffSamplesPerPixel,bpsPosition);
|
|
encodeRationalAtPosition(self,72.0,xResolutionPosition);
|
|
encodeRationalAtPosition(self,72.0,yResolutionPosition);
|
|
|
|
if(!lastImage)
|
|
putUnsigned32AtPosition(self,currentPosition(self),nextEntryOffsetPosition);
|
|
|
|
endBuffering(self);
|
|
}
|
|
|
|
void O2TIFFEncoderEnd(O2TIFFEncoderRef self) {
|
|
|
|
}
|