diff --git a/data/images/orient_0.webp b/data/images/orient_0.webp new file mode 100644 index 0000000..37c8a84 Binary files /dev/null and b/data/images/orient_0.webp differ diff --git a/data/images/orient_1.webp b/data/images/orient_1.webp new file mode 100644 index 0000000..de8274a Binary files /dev/null and b/data/images/orient_1.webp differ diff --git a/data/images/orient_2.webp b/data/images/orient_2.webp new file mode 100644 index 0000000..5256f98 Binary files /dev/null and b/data/images/orient_2.webp differ diff --git a/data/images/orient_3.webp b/data/images/orient_3.webp new file mode 100644 index 0000000..3c79b9b Binary files /dev/null and b/data/images/orient_3.webp differ diff --git a/data/images/orient_4.webp b/data/images/orient_4.webp new file mode 100644 index 0000000..00e75ef Binary files /dev/null and b/data/images/orient_4.webp differ diff --git a/data/images/orient_5.webp b/data/images/orient_5.webp new file mode 100644 index 0000000..fce6501 Binary files /dev/null and b/data/images/orient_5.webp differ diff --git a/data/images/orient_6.webp b/data/images/orient_6.webp new file mode 100644 index 0000000..f91dba3 Binary files /dev/null and b/data/images/orient_6.webp differ diff --git a/data/images/orient_7.webp b/data/images/orient_7.webp new file mode 100644 index 0000000..3a0c1db Binary files /dev/null and b/data/images/orient_7.webp differ diff --git a/data/images/orient_8.webp b/data/images/orient_8.webp new file mode 100644 index 0000000..df20d6f Binary files /dev/null and b/data/images/orient_8.webp differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5bc6d8f..1a182dc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,7 +24,7 @@ endif() if(XV_ENABLE_WEBP) add_compile_definitions(DOWEBP) - set(xv_libs ${xv_libs} WebP::libwebp) + set(xv_libs ${xv_libs} WebP::libwebp -lwebpdemux) endif() if(XV_ENABLE_JPEG) @@ -93,6 +93,7 @@ set(xv_sources xvmgcsfx.c xvmisc.c xvml.c + xvorient.c xvpbm.c xvpcd.c xvpcx.c diff --git a/src/xv.c b/src/xv.c index 4496495..b4a37e2 100644 --- a/src/xv.c +++ b/src/xv.c @@ -2311,7 +2311,7 @@ static int openPic(int filenum) pinfo.exifInfo = (byte *) NULL; pinfo.numpages = 1; pinfo.pagebname[0] = '\0'; - + pinfo.orientation = ORIENT_NONE; normaspect = defaspect; freename = dfltkludge = frompipe = frompoll = fromint = wascropped = 0; @@ -3552,6 +3552,8 @@ int ReadPicFile(char *fname, int ftype, PICINFO *pinfo, int quick) #endif } + + reorient_image(pinfo); return rv; } diff --git a/src/xv.h b/src/xv.h index 39b8564..e6537ef 100644 --- a/src/xv.h +++ b/src/xv.h @@ -447,9 +447,10 @@ #ifdef DOJPEG # define HAVE_JPEG -# ifdef DOEXIF -# define HAVE_EXIF -# endif +#endif + +#ifdef DOEXIF +# define HAVE_EXIF #endif #ifdef DOJP2K @@ -1130,6 +1131,7 @@ typedef struct { byte *pic; /* image data */ byte *exifInfo; /* image info from digicam */ int exifInfoSize; /* size of image info */ + int orientation; /* Which way up is the image? */ int numpages; /* # of page files, if >1 */ char pagebname[64]; /* basename of page files */ } PICINFO; @@ -1179,6 +1181,18 @@ typedef struct { int n; unsigned short int r; } MKT; +typedef enum orientation { + ORIENT_NONE = 0, + ORIENT_NORMAL = 1, + ORIENT_HFLIP = 2, + ORIENT_ROT180 = 3, + ORIENT_VFLIP = 4, + ORIENT_TRANSPOSE = 5, + ORIENT_ROT90 = 6, + ORIENT_TRANSVERSE = 7, + ORIENT_ROT270 = 8, +} orientation_t; + /* MACROS */ #define CENTERX(f,x,str) ((x)-XTextWidth(f,str, (int) strlen(str))/2) @@ -2162,6 +2176,10 @@ int MGCSFXSaveParams PARM((char *, int)); int getInputCom PARM((void)); int getOutputCom PARM((void)); +/*************************** XVORIENT.C *************************/ +unsigned int get_exif_orientation PARM((byte *buf, unsigned int bufsize)); +void reorient_image PARM((PICINFO *pinfo)); + /**************************** XVPBM.C ***************************/ #ifdef HAVE_MGCSFX int LoadPBM PARM((char *, PICINFO *, int)); diff --git a/src/xvjpeg.c b/src/xvjpeg.c index 0d0d353..daeb281 100644 --- a/src/xvjpeg.c +++ b/src/xvjpeg.c @@ -53,18 +53,6 @@ struct my_error_mgr { typedef struct my_error_mgr *my_error_ptr; -typedef enum orient { - ORIENT_NONE = 0, - ORIENT_NORMAL = 1, - ORIENT_HFLIP = 2, - ORIENT_ROT180 = 3, - ORIENT_VFLIP = 4, - ORIENT_TRANSPOSE = 5, - ORIENT_ROT90 = 6, - ORIENT_TRANSVERSE = 7, - ORIENT_ROT270 = 8, -} exif_orient_t; - /*** local functions ***/ static void drawJD PARM((int, int, int, int)); static void clickJD PARM((int, int)); @@ -97,7 +85,6 @@ static const char *fbasename = NULL; static char *comment = NULL; static byte *exifInfo = NULL; static int exifInfoSize = 0; /* not a string => must track size explicitly */ -static exif_orient_t exifOrientation = ORIENT_NONE; static int colorType; @@ -106,12 +93,6 @@ static BUTT jbut[J_NBUTTS]; char errbuffer[JMSG_LENGTH_MAX]; -#ifdef HAVE_EXIF -static ExifData *exif_data; -static ExifByteOrder exif_byte_order; -static ExifEntry *exif_entry; -#endif /* HAVE_EXIF */ - /***************************************************************************/ /* JPEG SAVE DIALOG ROUTINES ***********************************************/ @@ -530,7 +511,7 @@ int LoadJFIF(char *fname, PICINFO *pinfo, int quick) const char *colorspace_name; byte *pic; long filesize; - int i,w,h,bperpix,bperline,count,swap_xy=0; + int i,w,h,bperpix,bperline,count; /* Initialize variables below instead of in the declarations above to avoid the warning */ @@ -690,20 +671,7 @@ int LoadJFIF(char *fname, PICINFO *pinfo, int quick) jpeg_start_decompress(&cinfo); -#ifdef HAVE_EXIF - exif_data = exif_data_new_from_data(exifInfo, exifInfoSize); - exif_entry = exif_data_get_entry(exif_data, EXIF_TAG_ORIENTATION); - exif_byte_order = exif_data_get_byte_order(exif_data); - - /* If the EXIF IFD is as expected, get the orientation from it */ - if (exif_entry != NULL && - exif_entry->components == 1 && - exif_entry->size == 2 && - exif_entry->format == EXIF_FORMAT_SHORT) - { - exifOrientation = exif_get_short(exif_entry->data, exif_byte_order); - } -#endif + pinfo->orientation = get_exif_orientation(exifInfo, exifInfoSize); while (cinfo.output_scanline < cinfo.output_height) { #if 0 @@ -758,107 +726,11 @@ int LoadJFIF(char *fname, PICINFO *pinfo, int quick) pic = realloc(pic,p-pic); /* Release extra storage */ } - /* If we need to transform the image, allocate a new image and populate - ** it with the correct pixel values. Then swap the two images. - */ - if (exifOrientation != ORIENT_NONE && exifOrientation != ORIENT_NORMAL) - { - byte *tmppic; - byte *orientpic = (byte *) malloc((size_t) count); - int dst_row, dst_col, dst_row_width; - - if (!orientpic) { - SetISTR(ISTR_WARNING, "%s: can't transform JPEG file - out of memory", - fbasename); - goto L1; - } - - if (exifOrientation == ORIENT_VFLIP) - { - /* We can copy entire lines for VFLIP images, which will - ** be quicker than doing it pixel by pixel, so special - ** case this one. - */ - for (int row=0; row < h; row++) - { - memcpy(&orientpic[(h-row-1) * w * bperpix], - &pic[row * w * bperpix], - w * bperpix); - } - } else - { - int src_offset, dst_offset; - - - for (int row=0; row < h; row++) - { - for (int col=0; col < w; col++) - { - switch(exifOrientation) - { - case ORIENT_ROT90: - dst_col = h-row-1; - dst_row = col; - swap_xy = 1; - break; - case ORIENT_ROT180: - dst_col = w-col-1; - dst_row = h-row-1; - break; - case ORIENT_ROT270: - dst_col = row; - dst_row = w-col-1; - swap_xy = 1; - break; - case ORIENT_HFLIP: - dst_col = w-col-1; - dst_row = row; - break; - case ORIENT_TRANSPOSE: - dst_col = row; - dst_row = col; - swap_xy = 1; - break; - case ORIENT_TRANSVERSE: - dst_col = h-row-1; - dst_row = w-col-1; - swap_xy = 1; - break; - default: - dst_offset = src_offset; - break; - } - - dst_row_width = swap_xy ? h : w; - - src_offset = (row * w) + col; - dst_offset = (dst_row * dst_row_width) + dst_col; - - memcpy(&orientpic[dst_offset * bperpix], - &pic[src_offset * bperpix], - bperpix); - } - } - } - - tmppic = pic; - pic = orientpic; - free(tmppic); - } - - /* return 'PICINFO' structure to XV */ pinfo->pic = pic; - if (swap_xy) - { - pinfo->w = h; - pinfo->h = w; - } else - { - pinfo->w = w; - pinfo->h = h; - } + pinfo->w = w; + pinfo->h = h; pinfo->frmType = F_JPEG; if (cinfo.out_color_space == JCS_GRAYSCALE) { diff --git a/src/xvorient.c b/src/xvorient.c new file mode 100644 index 0000000..6c99565 --- /dev/null +++ b/src/xvorient.c @@ -0,0 +1,181 @@ +/* + * xvorient.c - handle the orientation flag in images (probably from EXIF) + */ + +#include "copyright.h" +#include "xv.h" + +#ifdef HAVE_EXIF +#include +#include +#endif /* HAVE_EXIF */ + +/**************************************************************/ +/**************************************************************/ + +unsigned int get_exif_orientation(byte *buf, unsigned int bufsize) +{ +#ifndef HAVE_EXIF + return ORIENT_NONE; +#else + ExifData *exif_data; + ExifEntry *exif_entry; + ExifByteOrder exif_byte_order; + + exif_data = exif_data_new_from_data(buf, bufsize); + if (exif_data == NULL) + { + return ORIENT_NONE; + } + + exif_entry = exif_data_get_entry(exif_data, EXIF_TAG_ORIENTATION); + if (exif_entry == NULL) + { + return ORIENT_NONE; + } + + exif_byte_order = exif_data_get_byte_order(exif_data); + + /* If the EXIF IFD is as expected, get the orientation from it */ + if ( + exif_entry->components == 1 && + exif_entry->size == 2 && + exif_entry->format == EXIF_FORMAT_SHORT) + { + return exif_get_short(exif_entry->data, exif_byte_order); + } + + return ORIENT_NONE; +#endif +} + +/**************************************************************/ +/**************************************************************/ + +void reorient_image(PICINFO *pinfo) +{ + byte *tmppic; + byte *orientpic; + int dst_row, dst_col, dst_row_width; + int bperpix, pixel_bytes, swap_xy, h, w; + + if (pinfo->orientation == ORIENT_NONE || pinfo->orientation == ORIENT_NORMAL) + { + /*** We don't need to do anything - the image is fine as it is ***/ + return; + } + + switch(pinfo->type) + { + case PIC8: + bperpix = 1; + break; + case PIC24: + bperpix = 3; + break; + + /* We don't know how many bytes we have per pixel, + ** so just give up here and leave the image as it is. + **/ + + default: + return; + } + + /* If we need to transform the image, allocate a new image and populate + ** it with the correct pixel values. Then swap the two images. + */ + + h = pinfo->h; + w = pinfo->w; + + pixel_bytes = h * w * bperpix; + + if ((orientpic = (byte *)malloc((size_t) pixel_bytes)) == NULL) + { + FatalError("unable to reorient image - out of memory"); + } + + if (pinfo->orientation == ORIENT_VFLIP) + { + /* We can copy entire lines for VFLIP images, which will + ** be quicker than doing it pixel by pixel, so special + ** case this one. + */ + for (int row=0; row < h; row++) + { + memcpy(&orientpic[(h-row-1) * w * bperpix], + &pinfo->pic[row * w * bperpix], + w * bperpix); + } + } else + { + int src_offset, dst_offset; + + for (int row=0; row < h; row++) + { + for (int col=0; col < w; col++) + { + switch(pinfo->orientation) + { + case ORIENT_ROT90: + dst_col = h-row-1; + dst_row = col; + swap_xy = 1; + break; + case ORIENT_ROT180: + dst_col = w-col-1; + dst_row = h-row-1; + break; + case ORIENT_ROT270: + dst_col = row; + dst_row = w-col-1; + swap_xy = 1; + break; + case ORIENT_HFLIP: + dst_col = w-col-1; + dst_row = row; + break; + case ORIENT_TRANSPOSE: + dst_col = row; + dst_row = col; + swap_xy = 1; + break; + case ORIENT_TRANSVERSE: + dst_col = h-row-1; + dst_row = w-col-1; + swap_xy = 1; + break; + default: + dst_offset = src_offset; + break; + } + + dst_row_width = swap_xy ? h : w; + + src_offset = (row * w) + col; + dst_offset = (dst_row * dst_row_width) + dst_col; + + memcpy(&orientpic[dst_offset * bperpix], + &pinfo->pic[src_offset * bperpix], + bperpix); + } + } + } + + /* Swap the images - make the reoriented image the + ** actual image and free the memory from the old image. + */ + tmppic = pinfo->pic; + pinfo->pic = orientpic; + free(tmppic); + + /* If we rotated the image in any way, + ** swap the w & h values in the pinfo. + */ + if (swap_xy) + { + pinfo->w = h; + pinfo->h = w; + } +} diff --git a/src/xvwebp.c b/src/xvwebp.c index 436d1c5..3808f7a 100644 --- a/src/xvwebp.c +++ b/src/xvwebp.c @@ -29,6 +29,7 @@ #include "webp/decode.h" #include "webp/encode.h" +#include "webp/demux.h" /* Used for xv to hand off save info to our 'library' */ static char *filename; @@ -476,6 +477,7 @@ int LoadWEBP(char *fname, PICINFO *pinfo) uint8_t *raw_data, *rgba, alpha; WebPBitstreamFeatures features; VP8StatusCode status; + uint32_t format_flags; /* open the file */ fp = xv_fopen(fname,"r"); @@ -644,6 +646,52 @@ int LoadWEBP(char *fname, PICINFO *pinfo) pinfo->pic[i*3 + 2] = *(rgba + i*4 + 2) * alpha/255; } +#ifdef HAVE_EXIF + /* If this image contains an EXIF orientation flag, + ** read it and set it appropriately in pinfo. + */ + + WebPDemuxer* demux; + WebPChunkIterator chunk_iter; + unsigned int exif_length; + byte *exif_data_src; + + demux = WebPDemux((WebPData *)&raw_data); + format_flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS); + + if (format_flags & EXIF_FLAG) + { + WebPDemuxGetChunk(demux, "EXIF", 1, &chunk_iter); + + /* The returned EXIF chunk contains the raw TIFF + ** image metadata. However, libexif expects it + ** to be prefixed with a "Exif\0\0" marker and + ** won't read it unless that's there. So put it + ** there and pretend that's what we read from + ** the WebP chunk. + */ + + exif_length = chunk_iter.chunk.size + 6; + + if ((exif_data_src = malloc(exif_length)) == NULL) + { + FatalError("malloc failure in LoadWEBP"); + } + + memcpy(exif_data_src, "Exif", 4); + memset(exif_data_src + 4, 0, 2); + memcpy(exif_data_src + 6, chunk_iter.chunk.bytes, chunk_iter.chunk.size); + + pinfo->orientation = get_exif_orientation(exif_data_src, chunk_iter.chunk.size + 6); + + free(exif_data_src); + } + + WebPDemuxReleaseChunkIterator(&chunk_iter); + WebPDemuxDelete(demux); + +#endif /* HAVE_EXIF */ + free(raw_data); WebPFree(rgba); return 1;