* Now honours the orientation as specified in the EXIF tags; not very well tested, so

it might happen that some rotations don't come out as expected...
* This orientation can be overridden with a special "exif:orientation" setting passed
  via the ioExtension message.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20635 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2007-04-10 13:36:53 +00:00
parent ddcd63762f
commit 9e34f742ae
1 changed files with 182 additions and 96 deletions

View File

@ -193,15 +193,48 @@ LoadSettings(jpeg_settings *settings)
} }
// #pragma mark - conversion routines static bool
x_flipped(int32 orientation)
{
return orientation == 2 || orientation == 3
|| orientation == 6 || orientation == 7;
}
static bool
y_flipped(int32 orientation)
{
return orientation == 3 || orientation == 4
|| orientation == 7 || orientation == 8;
}
static int32
dest_index(uint32 width, uint32 height, uint32 x, uint32 y, int32 orientation)
{
if (orientation > 4) {
uint32 temp = x;
x = y;
y = temp;
}
if (y_flipped(orientation))
y = height - 1 - y;
if (x_flipped(orientation))
x = width - 1 - x;
return y * width + x;
}
// #pragma mark - conversion for compression
inline void inline void
convert_from_gray1_to_gray8(uchar *in, uchar *out, int in_bytes) convert_from_gray1_to_gray8(uint8* in, uint8* out, int32 inRowBytes)
{ {
int32 index = 0; int32 index = 0;
int32 index2 = 0; int32 index2 = 0;
while (index < in_bytes) { while (index < inRowBytes) {
unsigned char c = in[index++]; unsigned char c = in[index++];
for (int b = 128; b; b = b>>1) { for (int b = 128; b; b = b>>1) {
unsigned char color; unsigned char color;
@ -216,11 +249,11 @@ convert_from_gray1_to_gray8(uchar *in, uchar *out, int in_bytes)
inline void inline void
convert_from_gray1_to_24(uchar *in, uchar *out, int in_bytes) convert_from_gray1_to_24(uint8* in, uint8* out, int32 inRowBytes)
{ {
int32 index = 0; int32 index = 0;
int32 index2 = 0; int32 index2 = 0;
while (index < in_bytes) { while (index < inRowBytes) {
unsigned char c = in[index++]; unsigned char c = in[index++];
for (int b = 128; b; b = b>>1) { for (int b = 128; b; b = b>>1) {
unsigned char color; unsigned char color;
@ -237,12 +270,12 @@ convert_from_gray1_to_24(uchar *in, uchar *out, int in_bytes)
inline void inline void
convert_from_cmap8_to_24(uchar *in, uchar *out, int in_bytes) convert_from_cmap8_to_24(uint8* in, uint8* out, int32 inRowBytes)
{ {
const color_map * map = system_colors(); const color_map * map = system_colors();
int32 index = 0; int32 index = 0;
int32 index2 = 0; int32 index2 = 0;
while (index < in_bytes) { while (index < inRowBytes) {
rgb_color color = map->color_list[in[index++]]; rgb_color color = map->color_list[in[index++]];
out[index2++] = color.red; out[index2++] = color.red;
@ -253,12 +286,12 @@ convert_from_cmap8_to_24(uchar *in, uchar *out, int in_bytes)
inline void inline void
convert_from_15_to_24(uchar *in, uchar *out, int in_bytes) convert_from_15_to_24(uint8* in, uint8* out, int32 inRowBytes)
{ {
int32 index = 0; int32 index = 0;
int32 index2 = 0; int32 index2 = 0;
int16 in_pixel; int16 in_pixel;
while (index < in_bytes) { while (index < inRowBytes) {
in_pixel = in[index] | (in[index+1] << 8); in_pixel = in[index] | (in[index+1] << 8);
index += 2; index += 2;
@ -270,12 +303,12 @@ convert_from_15_to_24(uchar *in, uchar *out, int in_bytes)
inline void inline void
convert_from_15b_to_24(uchar *in, uchar *out, int in_bytes) convert_from_15b_to_24(uint8* in, uint8* out, int32 inRowBytes)
{ {
int32 index = 0; int32 index = 0;
int32 index2 = 0; int32 index2 = 0;
int16 in_pixel; int16 in_pixel;
while (index < in_bytes) { while (index < inRowBytes) {
in_pixel = in[index+1] | (in[index] << 8); in_pixel = in[index+1] | (in[index] << 8);
index += 2; index += 2;
@ -287,12 +320,12 @@ convert_from_15b_to_24(uchar *in, uchar *out, int in_bytes)
inline void inline void
convert_from_16_to_24(uchar *in, uchar *out, int in_bytes) convert_from_16_to_24(uint8* in, uint8* out, int32 inRowBytes)
{ {
int32 index = 0; int32 index = 0;
int32 index2 = 0; int32 index2 = 0;
int16 in_pixel; int16 in_pixel;
while (index < in_bytes) { while (index < inRowBytes) {
in_pixel = in[index] | (in[index+1] << 8); in_pixel = in[index] | (in[index+1] << 8);
index += 2; index += 2;
@ -304,12 +337,12 @@ convert_from_16_to_24(uchar *in, uchar *out, int in_bytes)
inline void inline void
convert_from_16b_to_24(uchar *in, uchar *out, int in_bytes) convert_from_16b_to_24(uint8* in, uint8* out, int32 inRowBytes)
{ {
int32 index = 0; int32 index = 0;
int32 index2 = 0; int32 index2 = 0;
int16 in_pixel; int16 in_pixel;
while (index < in_bytes) { while (index < inRowBytes) {
in_pixel = in[index+1] | (in[index] << 8); in_pixel = in[index+1] | (in[index] << 8);
index += 2; index += 2;
@ -321,11 +354,11 @@ convert_from_16b_to_24(uchar *in, uchar *out, int in_bytes)
inline void inline void
convert_from_24_to_24(uchar *in, uchar *out, int in_bytes) convert_from_24_to_24(uint8* in, uint8* out, int32 inRowBytes)
{ {
int32 index = 0; int32 index = 0;
int32 index2 = 0; int32 index2 = 0;
while (index < in_bytes) { while (index < inRowBytes) {
out[index2++] = in[index+2]; out[index2++] = in[index+2];
out[index2++] = in[index+1]; out[index2++] = in[index+1];
out[index2++] = in[index]; out[index2++] = in[index];
@ -335,80 +368,94 @@ convert_from_24_to_24(uchar *in, uchar *out, int in_bytes)
inline void inline void
convert_from_32_to_24(uchar *in, uchar *out, int in_bytes) convert_from_32_to_24(uint8* in, uint8* out, int32 inRowBytes)
{ {
int32 index = 0; for (int32 i = 0; i < inRowBytes; i++) {
int32 index2 = 0; out[0] = in[2];
while (index < in_bytes) { out[1] = in[1];
out[index2++] = in[index+2]; out[2] = in[0];
out[index2++] = in[index+1];
out[index2++] = in[index]; in += 4;
index+=4; out += 3;
} }
} }
inline void inline void
convert_from_32b_to_24(uchar *in, uchar *out, int in_bytes) convert_from_32b_to_24(uint8* in, uint8* out, int32 inRowBytes)
{ {
int32 index = 0; for (int32 i = 0; i < inRowBytes; i++) {
int32 index2 = 0; out[1] = in[0];
while (index < in_bytes) { out[2] = in[1];
index++; out[3] = in[2];
out[index2++] = in[index++];
out[index2++] = in[index++]; in += 4;
out[index2++] = in[index++]; out += 3;
} }
} }
// #pragma mark - conversion for decompression
inline void inline void
convert_from_CMYK_to_32_photoshop(uchar *in, uchar *out, int out_bytes) convert_from_CMYK_to_32_photoshop(uint8* in, uint8* out, int32 inRowBytes, int32 xStep)
{ {
int32 index = 0; for (int32 i = 0; i < inRowBytes; i += 4) {
int32 index2 = 0; int32 black = in[3];
int32 black = 0; out[0] = in[2] * black / 255;
while (index < out_bytes) { out[1] = in[1] * black / 255;
black = in[index2+3]; out[2] = in[0] * black / 255;
out[index++] = in[index2+2]*black/255; out[3] = 255;
out[index++] = in[index2+1]*black/255;
out[index++] = in[index2]*black/255; in += 4;
out[index++] = 255; out += xStep;
index2 += 4;
} }
} }
//! !!! UNTESTED !!! //! !!! UNTESTED !!!
inline void inline void
convert_from_CMYK_to_32(uchar *in, uchar *out, int out_bytes) convert_from_CMYK_to_32(uint8* in, uint8* out, int32 inRowBytes, int32 xStep)
{ {
int32 index = 0; for (int32 i = 0; i < inRowBytes; i += 4) {
int32 index2 = 0; int32 black = 255 - in[3];
int32 black = 0; out[0] = ((255 - in[2]) * black) / 255;
while (index < out_bytes) { out[1] = ((255 - in[1]) * black) / 255;
black = 255 - in[index2+3]; out[2] = ((255 - in[0]) * black) / 255;
out[index++] = ((255-in[index2+2])*black)/255; out[3] = 255;
out[index++] = ((255-in[index2+1])*black)/255;
out[index++] = ((255-in[index2])*black)/255; in += 4;
out[index++] = 255; out += xStep;
index2 += 4;
} }
} }
//! RGB24 8:8:8 to xRGB 8:8:8:8 //! RGB24 8:8:8 to xRGB 8:8:8:8
inline void inline void
convert_from_24_to_32(uchar *in, uchar *out, int out_bytes) convert_from_24_to_32(uint8* in, uint8* out, int32 inRowBytes, int32 xStep)
{ {
int32 index = 0; for (int32 i = 0; i < inRowBytes; i += 3) {
int32 index2 = 0; out[0] = in[2];
while (index < out_bytes) { out[1] = in[1];
out[index++] = in[index2+2]; out[2] = in[0];
out[index++] = in[index2+1]; out[3] = 255;
out[index++] = in[index2];
out[index++] = 255; in += 3;
index2 += 3; out += xStep;
}
}
//! 8-bit to 8-bit, only need when rotating the image
void
translate_8(uint8* in, uint8* out, int32 inRowBytes, int32 xStep)
{
for (int32 i = 0; i < inRowBytes; i++) {
out[0] = in[0];
in++;
out += xStep;
} }
} }
@ -1134,7 +1181,8 @@ Compress(BPositionIO *in, BPositionIO *out)
// Function pointer to convert function // Function pointer to convert function
// It will point to proper function if needed // It will point to proper function if needed
void (*converter)(uchar *inscanline, uchar *outscanline, int inrow_bytes) = NULL; void (*converter)(uchar *inscanline, uchar *outscanline,
int32 inRowBytes) = NULL;
// Default color info // Default color info
J_COLOR_SPACE jpg_color_space = JCS_RGB; J_COLOR_SPACE jpg_color_space = JCS_RGB;
@ -1304,7 +1352,7 @@ Compress(BPositionIO *in, BPositionIO *out)
// Convert if needed // Convert if needed
if (converter != NULL) if (converter != NULL)
converter(in_scanline, out_scanline, in_row_bytes-padding); converter(in_scanline, out_scanline, in_row_bytes - padding);
// Write scanline // Write scanline
jpeg_write_scanlines(&cinfo, &writeline, 1); jpeg_write_scanlines(&cinfo, &writeline, 1);
@ -1331,8 +1379,10 @@ Decompress(BPositionIO *in, BPositionIO *out, BMessage* ioExtension)
jpeg_create_decompress(&cinfo); jpeg_create_decompress(&cinfo);
be_jpeg_stdio_src(&cinfo, in); be_jpeg_stdio_src(&cinfo, in);
#if 1
jpeg_save_markers(&cinfo, MARKER_EXIF, 131072); jpeg_save_markers(&cinfo, MARKER_EXIF, 131072);
// make sure the EXIF tag is stored // make sure the EXIF tag is stored
#endif
// Read info about image // Read info about image
jpeg_read_header(&cinfo, TRUE); jpeg_read_header(&cinfo, TRUE);
@ -1363,7 +1413,7 @@ Decompress(BPositionIO *in, BPositionIO *out, BMessage* ioExtension)
// Function pointer to convert function // Function pointer to convert function
// It will point to proper function if needed // It will point to proper function if needed
void (*converter)(uchar *inScanLine, uchar *outScanLine, void (*converter)(uchar *inScanLine, uchar *outScanLine,
int inRowBytes) = convert_from_24_to_32; int32 inRowBytes, int32 xStep) = convert_from_24_to_32;
// If color space isn't rgb // If color space isn't rgb
if (cinfo.out_color_space != JCS_RGB) { if (cinfo.out_color_space != JCS_RGB) {
@ -1377,7 +1427,7 @@ Decompress(BPositionIO *in, BPositionIO *out, BMessage* ioExtension)
// Grayscale // Grayscale
outColorSpace = B_GRAY8; outColorSpace = B_GRAY8;
outColorComponents = 1; outColorComponents = 1;
converter = NULL; converter = translate_8;
} else { } else {
// RGB // RGB
cinfo.out_color_space = JCS_RGB; cinfo.out_color_space = JCS_RGB;
@ -1409,20 +1459,42 @@ Decompress(BPositionIO *in, BPositionIO *out, BMessage* ioExtension)
// Initialize decompression // Initialize decompression
jpeg_start_decompress(&cinfo); jpeg_start_decompress(&cinfo);
// retrieve orientation from settings/EXIF
int32 orientation; int32 orientation;
if (exif.FindInt32("Orientation", &orientation) != B_OK) if (ioExtension == NULL
orientation = 1; || ioExtension->FindInt32("exif:orientation", &orientation) != B_OK) {
if (exif.FindInt32("Orientation", &orientation) != B_OK)
// Initialize this bounds rect to the size of your image orientation = 1;
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;
} }
if (orientation != 1 && converter == NULL)
converter = translate_8;
int32 outputWidth = orientation > 4 ? cinfo.output_height : cinfo.output_width;
int32 outputHeight = orientation > 4 ? cinfo.output_width : cinfo.output_height;
int32 destOffset = dest_index(outputWidth, outputHeight,
0, 0, orientation) * outColorComponents;
int32 xStep = dest_index(outputWidth, outputHeight,
1, 0, orientation) * outColorComponents - destOffset;
int32 yStep = dest_index(outputWidth, outputHeight,
0, 1, orientation) * outColorComponents - destOffset;
bool needAll = orientation != 1;
// Initialize this bounds rect to the size of your image
BRect bounds(0, 0, outputWidth - 1, outputHeight - 1);
#if 0
printf("destOffset = %ld, xStep = %ld, yStep = %ld, input: %ld x %ld, output: %ld x %ld, orientation %ld\n",
destOffset, xStep, yStep, (int32)cinfo.output_width, (int32)cinfo.output_height,
bounds.IntegerWidth() + 1, bounds.IntegerHeight() + 1, orientation);
#endif
// Bytes count in one line of image (scanline) // Bytes count in one line of image (scanline)
int64 rowBytes = cinfo.output_width * outColorComponents; int32 inRowBytes = cinfo.output_width * cinfo.output_components;
int32 rowBytes = (bounds.IntegerWidth() + 1) * outColorComponents;
int32 dataSize = cinfo.output_width * cinfo.output_height
* outColorComponents;
// Fill out the B_TRANSLATOR_BITMAP's header // Fill out the B_TRANSLATOR_BITMAP's header
TranslatorBitmap header; TranslatorBitmap header;
@ -1433,7 +1505,7 @@ Decompress(BPositionIO *in, BPositionIO *out, BMessage* ioExtension)
header.bounds.bottom = B_HOST_TO_BENDIAN_FLOAT(bounds.bottom); header.bounds.bottom = B_HOST_TO_BENDIAN_FLOAT(bounds.bottom);
header.colors = (color_space)B_HOST_TO_BENDIAN_INT32(outColorSpace); header.colors = (color_space)B_HOST_TO_BENDIAN_INT32(outColorSpace);
header.rowBytes = B_HOST_TO_BENDIAN_INT32(rowBytes); header.rowBytes = B_HOST_TO_BENDIAN_INT32(rowBytes);
header.dataSize = B_HOST_TO_BENDIAN_INT32(rowBytes * cinfo.output_height); header.dataSize = B_HOST_TO_BENDIAN_INT32(dataSize);
// Write out the header // Write out the header
status_t err = out->Write(&header, sizeof(TranslatorBitmap)); status_t err = out->Write(&header, sizeof(TranslatorBitmap));
@ -1443,39 +1515,53 @@ Decompress(BPositionIO *in, BPositionIO *out, BMessage* ioExtension)
return Error((j_common_ptr)&cinfo, B_ERROR); return Error((j_common_ptr)&cinfo, B_ERROR);
// Declare scanlines // Declare scanlines
JSAMPROW in_scanline = NULL; JSAMPROW inScanLine = NULL;
JSAMPROW out_scanline = NULL; uint8* dest = NULL;
JSAMPROW writeline; // Pointer to in_scanline or out_scanline (if there will be conversion) uint8* destLine = NULL;
// Allocate scanline // Allocate scanline
// Use libjpeg memory allocation functions, so in case of error it will free them itself // 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, inScanLine = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
JPOOL_PERMANENT, rowBytes); JPOOL_PERMANENT, inRowBytes);
// We need 2nd scanline storage only for conversion // We need 2nd scanline storage only for conversion
if (converter != NULL) { if (converter != NULL) {
// There will be conversion, allocate second scanline... // There will be conversion, allocate second scanline...
// Use libjpeg memory allocation functions, so in case of error it will free them itself // Use libjpeg memory allocation functions, so in case of error it will free
out_scanline = (unsigned char *)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo, // them itself
JPOOL_PERMANENT, rowBytes); dest = (uint8*)(cinfo.mem->alloc_large)((j_common_ptr)&cinfo,
// ... and make it the one to write to file JPOOL_PERMANENT, needAll ? dataSize : rowBytes);
writeline = out_scanline; destLine = dest + destOffset;
} else } else
writeline = in_scanline; destLine = inScanLine;
while (cinfo.output_scanline < cinfo.output_height) { while (cinfo.output_scanline < cinfo.output_height) {
// Read scanline // Read scanline
jpeg_read_scanlines(&cinfo, &in_scanline, 1); jpeg_read_scanlines(&cinfo, &inScanLine, 1);
// Convert if needed // Convert if needed
if (converter != NULL) if (converter != NULL)
converter(in_scanline, out_scanline, rowBytes); converter(inScanLine, destLine, inRowBytes, xStep);
// Write the scanline buffer to the output stream if (!needAll) {
err = out->Write(writeline, rowBytes); // Write the scanline buffer to the output stream
if (err < rowBytes) ssize_t bytesWritten = out->Write(destLine, rowBytes);
return err < B_OK ? Error((j_common_ptr)&cinfo, err) if (bytesWritten < rowBytes) {
return bytesWritten < B_OK
? Error((j_common_ptr)&cinfo, bytesWritten)
: Error((j_common_ptr)&cinfo, B_ERROR);
}
} else
destLine += yStep;
}
if (needAll) {
ssize_t bytesWritten = out->Write(dest, dataSize);
if (bytesWritten < dataSize) {
return bytesWritten < B_OK
? Error((j_common_ptr)&cinfo, bytesWritten)
: Error((j_common_ptr)&cinfo, B_ERROR); : Error((j_common_ptr)&cinfo, B_ERROR);
}
} }
jpeg_finish_decompress(&cinfo); jpeg_finish_decompress(&cinfo);