* Now also saves the EXIF data in the supplied message in Translate() (decompress

only, you cannot add EXIF data yet).
* Preparations to honour the orientation of JPEG images stored in the EXIF tags.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20633 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2007-04-10 10:54:44 +00:00
parent 8560c78809
commit 52e8f46af7
5 changed files with 421 additions and 29 deletions

View File

@ -32,9 +32,13 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "JPEGTranslator.h"
#include "exif_parser.h"
#include <TabView.h>
#define MARKER_EXIF 0xe1
// Set these accordingly
#define JPEG_ACRONYM "JPEG"
#define JPEG_FORMAT 'JPEG'
@ -79,6 +83,12 @@ translation_format outputFormats[] = {
{}
};
// Main functions of translator :)
static status_t Copy(BPositionIO *in, BPositionIO *out);
static status_t Compress(BPositionIO *in, BPositionIO *out);
static status_t Decompress(BPositionIO *in, BPositionIO *out, BMessage* ioExtension);
static status_t Error(j_common_ptr cinfo, status_t error = B_ERROR);
bool gAreSettingsRunning = false;
@ -1054,14 +1064,14 @@ Translate(BPositionIO *inSource, const translator_info *inInfo,
} else if (inInfo->type == B_TRANSLATOR_BITMAP && outType == JPEG_FORMAT) {
return Compress(inSource, outDestination);
} else if (inInfo->type == JPEG_FORMAT && outType == B_TRANSLATOR_BITMAP) {
return Decompress(inSource, outDestination);
return Decompress(inSource, outDestination, ioExtension);
}
return B_NO_TRANSLATOR;
}
/*! The user has requested the same format for input and output, so just copy */
status_t
static status_t
Copy(BPositionIO *in, BPositionIO *out)
{
int block_size = 65536;
@ -1095,7 +1105,7 @@ Copy(BPositionIO *in, BPositionIO *out)
/*! Encode into the native format */
status_t
static status_t
Compress(BPositionIO *in, BPositionIO *out)
{
// Load Settings
@ -1307,8 +1317,8 @@ Compress(BPositionIO *in, BPositionIO *out)
/*! Decode the native format */
status_t
Decompress(BPositionIO *in, BPositionIO *out)
static status_t
Decompress(BPositionIO *in, BPositionIO *out, BMessage* ioExtension)
{
// Load Settings
jpeg_settings settings;
@ -1321,16 +1331,39 @@ Decompress(BPositionIO *in, BPositionIO *out)
jpeg_create_decompress(&cinfo);
be_jpeg_stdio_src(&cinfo, in);
jpeg_save_markers(&cinfo, MARKER_EXIF, 131072);
// make sure the EXIF tag is stored
// Read info about image
jpeg_read_header(&cinfo, TRUE);
BMessage exif;
if (ioExtension != NULL) {
// add EXIF data to message, if any
jpeg_marker_struct* marker = cinfo.marker_list;
while (marker != NULL) {
if (marker->marker == MARKER_EXIF
&& !strncmp((char*)marker->data, "Exif", 4)) {
// Strip EXIF header from TIFF data
ioExtension->AddData("exif", B_RAW_TYPE,
(uint8 *)marker->data + 6, marker->data_length - 6);
BMemoryIO io(marker->data + 6, marker->data_length - 6);
convert_exif_to_message(io, exif);
}
marker = marker->next;
}
}
// Default color info
color_space out_color_space = B_RGB32;
int out_color_components = 4;
color_space outColorSpace = B_RGB32;
int outColorComponents = 4;
// Function pointer to convert function
// It will point to proper function if needed
void (*converter)(uchar *inscanline, uchar *outscanline, int inrow_bytes) = convert_from_24_to_32;
void (*converter)(uchar *inScanLine, uchar *outScanLine,
int inRowBytes) = convert_from_24_to_32;
// If color space isn't rgb
if (cinfo.out_color_space != JCS_RGB) {
@ -1342,8 +1375,8 @@ Decompress(BPositionIO *in, BPositionIO *out)
// Check if user wants to read only as RGB32 or not
if (!settings.Always_B_RGB32) {
// Grayscale
out_color_space = B_GRAY8;
out_color_components = 1;
outColorSpace = B_GRAY8;
outColorComponents = 1;
converter = NULL;
} else {
// RGB
@ -1376,12 +1409,21 @@ Decompress(BPositionIO *in, BPositionIO *out)
// Initialize decompression
jpeg_start_decompress(&cinfo);
// !!! Initialize this bounds rect to the size of your image
BRect bounds(0, 0, cinfo.output_width-1, cinfo.output_height-1);
int32 orientation;
if (exif.FindInt32("Orientation", &orientation) != B_OK)
orientation = 1;
// Initialize this bounds rect to the size of your image
BRect bounds(0, 0, cinfo.output_width - 1, cinfo.output_height - 1);
if (orientation & 4) {
// image is rotated
bounds.right = cinfo.output_height - 1;
bounds.bottom = cinfo.output_width - 1;
}
// Bytes count in one line of image (scanline)
int64 row_bytes = cinfo.output_width * out_color_components;
int64 rowBytes = cinfo.output_width * outColorComponents;
// Fill out the B_TRANSLATOR_BITMAP's header
TranslatorBitmap header;
header.magic = B_HOST_TO_BENDIAN_INT32(B_TRANSLATOR_BITMAP);
@ -1389,9 +1431,9 @@ Decompress(BPositionIO *in, BPositionIO *out)
header.bounds.top = B_HOST_TO_BENDIAN_FLOAT(bounds.top);
header.bounds.right = B_HOST_TO_BENDIAN_FLOAT(bounds.right);
header.bounds.bottom = B_HOST_TO_BENDIAN_FLOAT(bounds.bottom);
header.colors = (color_space)B_HOST_TO_BENDIAN_INT32(out_color_space);
header.rowBytes = B_HOST_TO_BENDIAN_INT32(row_bytes);
header.dataSize = B_HOST_TO_BENDIAN_INT32(row_bytes * cinfo.output_height);
header.colors = (color_space)B_HOST_TO_BENDIAN_INT32(outColorSpace);
header.rowBytes = B_HOST_TO_BENDIAN_INT32(rowBytes);
header.dataSize = B_HOST_TO_BENDIAN_INT32(rowBytes * cinfo.output_height);
// Write out the header
status_t err = out->Write(&header, sizeof(TranslatorBitmap));
@ -1408,14 +1450,14 @@ Decompress(BPositionIO *in, BPositionIO *out)
// Allocate scanline
// Use libjpeg memory allocation functions, so in case of error it will free them itself
in_scanline = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
JPOOL_PERMANENT, row_bytes);
JPOOL_PERMANENT, rowBytes);
// We need 2nd scanline storage only for conversion
if (converter != NULL) {
// There will be conversion, allocate second scanline...
// Use libjpeg memory allocation functions, so in case of error it will free them itself
out_scanline = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
JPOOL_PERMANENT, row_bytes);
JPOOL_PERMANENT, rowBytes);
// ... and make it the one to write to file
writeline = out_scanline;
} else
@ -1427,11 +1469,11 @@ Decompress(BPositionIO *in, BPositionIO *out)
// Convert if needed
if (converter != NULL)
converter(in_scanline, out_scanline, row_bytes);
converter(in_scanline, out_scanline, rowBytes);
// Write the scanline buffer to the output stream
err = out->Write(writeline, row_bytes);
if (err < row_bytes)
err = out->Write(writeline, rowBytes);
if (err < rowBytes)
return err < B_OK ? Error((j_common_ptr)&cinfo, err)
: Error((j_common_ptr)&cinfo, B_ERROR);
}
@ -1445,7 +1487,7 @@ Decompress(BPositionIO *in, BPositionIO *out)
Frees jpeg alocated memory
Returns given error (B_ERROR by default)
*/
status_t
static status_t
Error(j_common_ptr cinfo, status_t error)
{
jpeg_destroy(cinfo);

View File

@ -215,10 +215,4 @@ EXTERN(void) be_jpeg_stdio_dest(j_compress_ptr cinfo, BPositionIO *outfile); //
EXTERN(struct jpeg_error_mgr *) be_jpeg_std_error (struct jpeg_error_mgr * err, jpeg_settings * settings); // from "be_jerror.cpp"
// Main functions of translator :)
status_t Copy(BPositionIO *in, BPositionIO *out);
status_t Compress(BPositionIO *in, BPositionIO *out);
status_t Decompress(BPositionIO *in, BPositionIO *out);
status_t Error(j_common_ptr cinfo, status_t error = B_ERROR);
#endif // _JPEGTRANSLATOR_H_

View File

@ -9,6 +9,9 @@ SubDirC++Flags [ FDefines BEOS_R5_COMPATIBLE ] ;
SubDirSysHdrs [ FDirName $(SUBDIR) libjpeg ] ;
SubDirSysHdrs [ FDirName $(SUBDIR) $(DOTDOT) raw ] ;
# for TIFF.h and ReadHelper.h
local jpeg_files =
# libjpeg
jcapimin.c
@ -82,6 +85,7 @@ Translator JPEGTranslator :
be_jdatadst.cpp
be_jdatasrc.cpp
be_jerror.cpp
exif_parser.cpp
JPEGTranslator.cpp
$(jpeg_files)

View File

@ -0,0 +1,327 @@
/*
* Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include "exif_parser.h"
#include <ReadHelper.h>
#include <Message.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
enum {
TAG_EXIF_OFFSET = 0x8769,
TAG_SUB_DIR_OFFSET = 0xa005,
TAG_MAKER = 0x10f,
TAG_MODEL = 0x110,
TAG_ORIENTATION = 0x112,
TAG_EXPOSURE_TIME = 0x829a,
TAG_ISO = 0x8827,
};
static const convert_tag kDefaultTags[] = {
{TAG_MAKER, B_ANY_TYPE, "Maker"},
{TAG_MODEL, B_ANY_TYPE, "Model"},
{TAG_ORIENTATION, B_INT32_TYPE, "Orientation"},
{TAG_EXPOSURE_TIME, B_DOUBLE_TYPE, "ExposureTime"},
{TAG_ISO, B_INT32_TYPE, "ISO"},
};
static const size_t kNumDefaultTags = sizeof(kDefaultTags)
/ sizeof(kDefaultTags[0]);
static status_t parse_tiff_directory(TReadHelper& read, BMessage& target,
const convert_tag* tags, size_t tagCount);
static status_t
add_to_message(TReadHelper& source, BMessage& target, tiff_tag& tag,
const char* name, type_code type)
{
type_code defaultType = B_INT32_TYPE;
double doubleValue = 0.0;
int32 intValue = 0;
switch (tag.type) {
case TIFF_STRING_TYPE:
{
if (type != B_ANY_TYPE && type != B_STRING_TYPE)
return B_BAD_VALUE;
char* buffer = (char*)malloc(tag.length);
if (buffer == NULL)
return B_NO_MEMORY;
source(buffer, tag.length);
// remove trailing spaces
int32 i = tag.length;
while (--i > 0 && isspace(buffer[i]) || !buffer[i]) {
buffer[i] = '\0';
}
status_t status = target.AddString(name, buffer);
free(buffer);
return status;
}
case TIFF_UNDEFINED_TYPE:
{
if (type != B_ANY_TYPE && type != B_STRING_TYPE && type != B_RAW_TYPE)
return B_BAD_VALUE;
char* buffer = (char*)malloc(tag.length);
if (buffer == NULL)
return B_NO_MEMORY;
source(buffer, tag.length);
status_t status;
if (type == B_STRING_TYPE)
status = target.AddString(name, buffer);
else
status = target.AddData(name, B_RAW_TYPE, buffer, tag.length);
free(buffer);
return status;
}
// unsigned
case TIFF_UINT8_TYPE:
intValue = source.Next<uint8>();
break;
case TIFF_UINT16_TYPE:
defaultType = B_INT32_TYPE;
intValue = source.Next<uint16>();
break;
case TIFF_UINT32_TYPE:
defaultType = B_INT32_TYPE;
intValue = source.Next<uint32>();
break;
case TIFF_UFRACTION_TYPE:
{
defaultType = B_DOUBLE_TYPE;
double value = source.Next<uint32>();
doubleValue = value / source.Next<uint32>();
break;
}
// signed
case TIFF_INT8_TYPE:
intValue = source.Next<int8>();
break;
case TIFF_INT16_TYPE:
intValue = source.Next<int16>();
break;
case TIFF_INT32_TYPE:
intValue = source.Next<int32>();
break;
case TIFF_FRACTION_TYPE:
{
defaultType = B_DOUBLE_TYPE;
double value = source.Next<int32>();
doubleValue = value / source.Next<int32>();
}
// floating point
case TIFF_FLOAT_TYPE:
defaultType = B_FLOAT_TYPE;
doubleValue = source.Next<float>();
break;
case TIFF_DOUBLE_TYPE:
defaultType = B_DOUBLE_TYPE;
doubleValue = source.Next<double>();
break;
default:
return B_BAD_VALUE;
}
if (defaultType == B_INT32_TYPE)
doubleValue = intValue;
else
intValue = int32(doubleValue + 0.5);
if (type == B_ANY_TYPE)
type = defaultType;
switch (type) {
case B_INT32_TYPE:
return target.AddInt32(name, intValue);
case B_FLOAT_TYPE:
return target.AddFloat(name, doubleValue);
case B_DOUBLE_TYPE:
return target.AddDouble(name, doubleValue);
default:
return B_BAD_VALUE;
}
}
static const convert_tag*
find_convert_tag(uint16 id, const convert_tag* tags, size_t count)
{
for (size_t i = 0; i < count; i++) {
if (tags[i].tag == id)
return &tags[i];
}
return NULL;
}
/*!
Reads a TIFF tag and positions the file stream to its data section
*/
void
parse_tiff_tag(TReadHelper& read, tiff_tag& tag, off_t& offset)
{
read(tag.tag);
read(tag.type);
read(tag.length);
offset = read.Position() + 4;
uint32 length = tag.length;
switch (tag.type) {
case TIFF_UINT16_TYPE:
case TIFF_INT16_TYPE:
length *= 2;
break;
case TIFF_UINT32_TYPE:
case TIFF_INT32_TYPE:
case TIFF_FLOAT_TYPE:
length *= 4;
break;
case TIFF_UFRACTION_TYPE:
case TIFF_FRACTION_TYPE:
case TIFF_DOUBLE_TYPE:
length *= 8;
break;
default:
break;
}
if (length > 4) {
uint32 position;
read(position);
read.Seek(position, SEEK_SET);
}
}
static status_t
parse_tiff_directory(TReadHelper& read, off_t offset, BMessage& target,
const convert_tag* convertTags, size_t convertTagCount)
{
read.Seek(offset, SEEK_SET);
uint16 tags;
read(tags);
if (tags > 512)
return B_BAD_DATA;
while (tags--) {
off_t nextOffset;
tiff_tag tag;
parse_tiff_tag(read, tag, nextOffset);
//printf("TAG %u\n", tag.tag);
switch (tag.tag) {
case TAG_EXIF_OFFSET:
case TAG_SUB_DIR_OFFSET:
{
status_t status = parse_tiff_directory(read, target,
convertTags, convertTagCount);
if (status < B_OK)
return status;
break;
}
default:
const convert_tag* convertTag = find_convert_tag(tag.tag,
convertTags, convertTagCount);
if (convertTag != NULL) {
add_to_message(read, target, tag, convertTag->name,
convertTag->type);
}
break;
}
read.Seek(nextOffset, SEEK_SET);
}
return B_OK;
}
static status_t
parse_tiff_directory(TReadHelper& read, BMessage& target,
const convert_tag* tags, size_t tagCount)
{
while (true) {
int32 offset;
read(offset);
if (offset == 0)
break;
status_t status = parse_tiff_directory(read, offset, target,
tags, tagCount);
if (status < B_OK)
return status;
}
return B_OK;
}
// #pragma mark -
status_t
convert_exif_to_message(BPositionIO& source, BMessage& target,
const convert_tag* tags, size_t tagCount)
{
TReadHelper read(source);
uint16 endian;
read(endian);
if (endian != 'MM' && endian != 'II')
return B_BAD_TYPE;
#if B_HOST_IS_LENDIAN
read.SetSwap(endian == 'MM');
#else
read.SetSwap(endian == 'II');
#endif
int16 magic;
read(magic);
if (magic != 42)
return B_BAD_TYPE;
return parse_tiff_directory(read, target, tags, tagCount);
}
status_t
convert_exif_to_message(BPositionIO& source, BMessage& target)
{
return convert_exif_to_message(source, target, kDefaultTags,
kNumDefaultTags);
}

View File

@ -0,0 +1,25 @@
/*
* Copyright 2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef EXIF_PARSER_H
#define EXIF_PARSER_H
#include <DataIO.h>
#include <TypeConstants.h>
class BMessage;
struct convert_tag {
uint16 tag;
type_code type;
const char* name;
};
status_t convert_exif_to_message(BPositionIO& source, BMessage& target);
status_t convert_exif_to_message_etc(BPositionIO& source, BMessage& target,
const convert_tag* tags, size_t tagCount);
#endif // EXIF_PARSER_H