/* Adjust Exif image parameters. * * We try to adjust the Tags ExifImageWidth and ExifImageHeight if possible. */ LOCAL(void) adjust_exif_parameters (JOCTET FAR * data, unsigned int length, JDIMENSION new_width, JDIMENSION new_height) { boolean is_motorola; /* Flag for byte order */ unsigned int number_of_tags, tagnum; unsigned int firstoffset, offset; JDIMENSION new_value; if (length < 12) return; /* Length of an IFD entry */ /* Discover byte order */ if (GETJOCTET(data[0]) == 0x49 && GETJOCTET(data[1]) == 0x49) is_motorola = FALSE; else if (GETJOCTET(data[0]) == 0x4D && GETJOCTET(data[1]) == 0x4D) is_motorola = TRUE; else return; /* Check Tag Mark */ if (is_motorola) { if (GETJOCTET(data[2]) != 0) return; if (GETJOCTET(data[3]) != 0x2A) return; } else { if (GETJOCTET(data[3]) != 0) return; if (GETJOCTET(data[2]) != 0x2A) return; } /* Get first IFD offset (offset to IFD0) */ if (is_motorola) { if (GETJOCTET(data[4]) != 0) return; if (GETJOCTET(data[5]) != 0) return; firstoffset = GETJOCTET(data[6]); firstoffset <<= 8; firstoffset += GETJOCTET(data[7]); } else { if (GETJOCTET(data[7]) != 0) return; if (GETJOCTET(data[6]) != 0) return; firstoffset = GETJOCTET(data[5]); firstoffset <<= 8; firstoffset += GETJOCTET(data[4]); } if (firstoffset > length - 2) return; /* check end of data segment */ /* Get the number of directory entries contained in this IFD */ if (is_motorola) { number_of_tags = GETJOCTET(data[firstoffset]); number_of_tags <<= 8; number_of_tags += GETJOCTET(data[firstoffset+1]); } else { number_of_tags = GETJOCTET(data[firstoffset+1]); number_of_tags <<= 8; number_of_tags += GETJOCTET(data[firstoffset]); } if (number_of_tags == 0) return; firstoffset += 2; /* Search for ExifSubIFD offset Tag in IFD0 */ for (;;) { if (firstoffset > length - 12) return; /* check end of data segment */ /* Get Tag number */ if (is_motorola) { tagnum = GETJOCTET(data[firstoffset]); tagnum <<= 8; tagnum += GETJOCTET(data[firstoffset+1]); } else { tagnum = GETJOCTET(data[firstoffset+1]); tagnum <<= 8; tagnum += GETJOCTET(data[firstoffset]); } if (tagnum == 0x8769) break; /* found ExifSubIFD offset Tag */ if (--number_of_tags == 0) return; firstoffset += 12; } /* Get the ExifSubIFD offset */ if (is_motorola) { if (GETJOCTET(data[firstoffset+8]) != 0) return; if (GETJOCTET(data[firstoffset+9]) != 0) return; offset = GETJOCTET(data[firstoffset+10]); offset <<= 8; offset += GETJOCTET(data[firstoffset+11]); } else { if (GETJOCTET(data[firstoffset+11]) != 0) return; if (GETJOCTET(data[firstoffset+10]) != 0) return; offset = GETJOCTET(data[firstoffset+9]); offset <<= 8; offset += GETJOCTET(data[firstoffset+8]); } if (offset > length - 2) return; /* check end of data segment */ /* Get the number of directory entries contained in this SubIFD */ if (is_motorola) { number_of_tags = GETJOCTET(data[offset]); number_of_tags <<= 8; number_of_tags += GETJOCTET(data[offset+1]); } else { number_of_tags = GETJOCTET(data[offset+1]); number_of_tags <<= 8; number_of_tags += GETJOCTET(data[offset]); } if (number_of_tags < 2) return; offset += 2; /* Search for ExifImageWidth and ExifImageHeight Tags in this SubIFD */ do { if (offset > length - 12) return; /* check end of data segment */ /* Get Tag number */ if (is_motorola) { tagnum = GETJOCTET(data[offset]); tagnum <<= 8; tagnum += GETJOCTET(data[offset+1]); } else { tagnum = GETJOCTET(data[offset+1]); tagnum <<= 8; tagnum += GETJOCTET(data[offset]); } if (tagnum == 0xA002 || tagnum == 0xA003) { if (tagnum == 0xA002) new_value = new_width; /* ExifImageWidth Tag */ else new_value = new_height; /* ExifImageHeight Tag */ if (is_motorola) { data[offset+2] = 0; /* Format = unsigned long (4 octets) */ data[offset+3] = 4; data[offset+4] = 0; /* Number Of Components = 1 */ data[offset+5] = 0; data[offset+6] = 0; data[offset+7] = 1; data[offset+8] = 0; data[offset+9] = 0; data[offset+10] = (JOCTET)((new_value >> 8) & 0xFF); data[offset+11] = (JOCTET)(new_value & 0xFF); } else { data[offset+2] = 4; /* Format = unsigned long (4 octets) */ data[offset+3] = 0; data[offset+4] = 1; /* Number Of Components = 1 */ data[offset+5] = 0; data[offset+6] = 0; data[offset+7] = 0; data[offset+8] = (JOCTET)(new_value & 0xFF); data[offset+9] = (JOCTET)((new_value >> 8) & 0xFF); data[offset+10] = 0; data[offset+11] = 0; } } offset += 12; } while (--number_of_tags); } /* Adjust output image parameters as needed. * * This must be called after jpeg_copy_critical_parameters() * and before jpeg_write_coefficients(). * * The return value is the set of virtual coefficient arrays to be written * (either the ones allocated by jtransform_request_workspace, or the * original source data arrays). The caller will need to pass this value * to jpeg_write_coefficients(). */ GLOBAL(jvirt_barray_ptr *) jtransform_adjust_parameters (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, jvirt_barray_ptr *src_coef_arrays, jpeg_transform_info *info) { /* If force-to-grayscale is requested, adjust destination parameters */ if (info->force_grayscale) { /* We use jpeg_set_colorspace to make sure subsidiary settings get fixed * properly. Among other things, the target h_samp_factor & v_samp_factor * will get set to 1, which typically won't match the source. * In fact we do this even if the source is already grayscale; that * provides an easy way of coercing a grayscale JPEG with funny sampling * factors to the customary 1,1. (Some decoders fail on other factors.) */ if ((dstinfo->jpeg_color_space == JCS_YCbCr && dstinfo->num_components == 3) || (dstinfo->jpeg_color_space == JCS_GRAYSCALE && dstinfo->num_components == 1)) { /* We have to preserve the source's quantization table number. */ int sv_quant_tbl_no = dstinfo->comp_info[0].quant_tbl_no; jpeg_set_colorspace(dstinfo, JCS_GRAYSCALE); dstinfo->comp_info[0].quant_tbl_no = sv_quant_tbl_no; } else { /* Sorry, can't do it */ ERREXIT(dstinfo, JERR_CONVERSION_NOTIMPL); } } /* Correct the destination's image dimensions etc if necessary */ switch (info->transform) { case JXFORM_NONE: /* Nothing to do */ break; case JXFORM_FLIP_H: if (info->trim) trim_right_edge(dstinfo); break; case JXFORM_FLIP_V: if (info->trim) trim_bottom_edge(dstinfo); break; case JXFORM_TRANSPOSE: transpose_critical_parameters(dstinfo); /* transpose does NOT have to trim anything */ break; case JXFORM_TRANSVERSE: transpose_critical_parameters(dstinfo); if (info->trim) { trim_right_edge(dstinfo); trim_bottom_edge(dstinfo); } break; case JXFORM_ROT_90: transpose_critical_parameters(dstinfo); if (info->trim) trim_right_edge(dstinfo); break; case JXFORM_ROT_180: if (info->trim) { trim_right_edge(dstinfo); trim_bottom_edge(dstinfo); } break; case JXFORM_ROT_270: transpose_critical_parameters(dstinfo); if (info->trim) trim_bottom_edge(dstinfo); break; } /* Adjust Exif properties */ if (srcinfo->marker_list != NULL && srcinfo->marker_list->marker == JPEG_APP0+1 && srcinfo->marker_list->data_length >= 6 && GETJOCTET(srcinfo->marker_list->data[0]) == 0x45 && GETJOCTET(srcinfo->marker_list->data[1]) == 0x78 && GETJOCTET(srcinfo->marker_list->data[2]) == 0x69 && GETJOCTET(srcinfo->marker_list->data[3]) == 0x66 && GETJOCTET(srcinfo->marker_list->data[4]) == 0 && GETJOCTET(srcinfo->marker_list->data[5]) == 0) { /* Suppress output of JFIF marker */ dstinfo->write_JFIF_header = FALSE; /* Adjust Exif image parameters */ if (dstinfo->image_width != srcinfo->image_width || dstinfo->image_height != srcinfo->image_height) /* Align data segment to start of TIFF structure for parsing */ adjust_exif_parameters(srcinfo->marker_list->data + 6, srcinfo->marker_list->data_length - 6, dstinfo->image_width, dstinfo->image_height); } /* Return the appropriate output data set */ if (info->workspace_coef_arrays != NULL) return info->workspace_coef_arrays; return src_coef_arrays; }