Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix eos imageformat #1031

Merged
merged 3 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 130 additions & 23 deletions camlibs/ptp2/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -848,30 +848,30 @@ _put_Generic##bits##Table(CONFIG_PUT_ARGS, struct deviceproptable##bits * tbl, i
if (dpd->FormFlag & PTP_DPFF_Enumeration) { \
for (j = 0; j<dpd->FORM.Enum.NumberOfValues; j++) { \
if (bits##val == dpd->FORM.Enum.SupportedValue[j].bits) { \
GP_LOG_D ("FOUND right value for %s in the enumeration at val %d", value, bits##val); \
GP_LOG_D ("FOUND right value for %s in the enumeration at val %04x", value, bits##val); \
propval->bits = bits##val; \
return GP_OK; \
} \
} \
GP_LOG_D ("did not find the right value for %s in the enumeration at val %d... continuing", value, bits##val); \
GP_LOG_D ("did not find the right value for %s in the enumeration at val %04x... continuing", value, bits##val); \
/* continue looking, but with this value as fallback */ \
} else { \
GP_LOG_D ("not an enumeration ... return %s as %d", value, bits##val); \
GP_LOG_D ("not an enumeration ... return %s as %04x", value, bits##val); \
propval->bits = bits##val; \
return GP_OK; \
} \
} \
} \
if (foundvalue) { \
GP_LOG_D ("Using fallback, not found in enum... return %s as %d", value, bits##val); \
GP_LOG_D ("Using fallback, not found in enum... return %s as %04x", value, bits##val); \
propval->bits = bits##val; \
return GP_OK; \
} \
if (!sscanf(value, _("Unknown value %04x"), &intval)) { \
GP_LOG_E ("failed to find value %s in list", value); \
return GP_ERROR; \
} \
GP_LOG_D ("Using fallback, not found in enum... return %s as %d", value, bits##val); \
GP_LOG_D ("Using fallback, not found in enum... return %s as %04x", value, bits##val); \
propval->bits = intval; \
return GP_OK; \
}
Expand Down Expand Up @@ -2687,20 +2687,20 @@ GENERIC16TABLE(Canon_ISO,canon_isospeed)

/* see ptp-pack.c:ptp_unpack_EOS_ImageFormat */
static struct deviceproptableu16 canon_eos_image_format[] = {
{ N_("RAW"), 0x0c00, 0 },
{ N_("mRAW"), 0x1c00, 0 },
{ N_("sRAW"), 0x2c00, 0 },
{ N_("cRAW"), 0x0b00, 0 },
{ N_("Large Fine JPEG"), 0x0300, 0 },
{ N_("Large Normal JPEG"), 0x0200, 0 },
{ N_("Medium Fine JPEG"), 0x1300, 0 },
{ N_("Medium Normal JPEG"), 0x1200, 0 },
{ N_("Small Fine JPEG"), 0x2300, 0 },
{ N_("Small Normal JPEG"), 0x2200, 0 },
{ N_("Small Fine JPEG"), 0xd300, 0 },
{ N_("Small Normal JPEG"), 0xd200, 0 },
{ N_("Smaller JPEG"), 0xe300, 0 },
{ N_("Tiny JPEG"), 0xf300, 0 },
{ N_("RAW"), 0x0cff, 0 },
{ N_("mRAW"), 0x1cff, 0 },
{ N_("sRAW"), 0x2cff, 0 },
{ N_("cRAW"), 0x0bff, 0 },
{ N_("Large Fine JPEG"), 0x03ff, 0 },
{ N_("Large Normal JPEG"), 0x02ff, 0 },
{ N_("Medium Fine JPEG"), 0x13ff, 0 },
{ N_("Medium Normal JPEG"), 0x12ff, 0 },
{ N_("Small Fine JPEG"), 0x23ff, 0 },
{ N_("Small Normal JPEG"), 0x22ff, 0 },
{ N_("Small Fine JPEG"), 0xd3ff, 0 },
{ N_("Small Normal JPEG"), 0xd2ff, 0 },
{ N_("Smaller JPEG"), 0xe3ff, 0 },
{ N_("Tiny JPEG"), 0xf3ff, 0 },
{ N_("RAW + Large Fine JPEG"), 0x0c03, 0 },
{ N_("mRAW + Large Fine JPEG"), 0x1c03, 0 },
{ N_("sRAW + Large Fine JPEG"), 0x2c03, 0 },
Expand Down Expand Up @@ -2741,10 +2741,10 @@ static struct deviceproptableu16 canon_eos_image_format[] = {
/* There are more RAW + 'smallish' JPEG combinations for at least the 5DM3 possible.
Axel was simply to lazy to exercise the combinatorial explosion. :-/ */
/* 1DX series 0 compression options */
{ N_("Small"), 0x2100, 0 },
{ N_("Medium 1"), 0x5100, 0 },
{ N_("Medium 2"), 0x6100, 0 },
{ N_("Large"), 0x0100, 0 },
{ N_("Small"), 0x21ff, 0 },
{ N_("Medium 1"), 0x51ff, 0 },
{ N_("Medium 2"), 0x61ff, 0 },
{ N_("Large"), 0x01ff, 0 },
{ N_("Small + RAW"), 0x0c21, 0 },
{ N_("Medium 1 + RAW"), 0x0c51, 0 },
{ N_("Medium 2 + RAW"), 0x0c61, 0 },
Expand All @@ -2766,6 +2766,112 @@ static struct deviceproptableu16 canon_eos_image_format[] = {
};
GENERIC16TABLE(Canon_EOS_ImageFormat,canon_eos_image_format)

static struct deviceproptableu8 canon_eos_single_ImageFormats[] = {
{ N_("RAW"), 0x0c, 0 },
{ N_("mRAW"), 0x1c, 0 },
{ N_("sRAW"), 0x2c, 0 },
{ N_("cRAW"), 0x0b, 0 },

{ N_("L"), 0x03, 0 },
{ N_("M"), 0x13, 0 },
{ N_("S"), 0x23, 0 },
{ N_("cL"), 0x02, 0 },
{ N_("cM"), 0x12, 0 },
{ N_("cS"), 0x22, 0 },
/* e.g. 5Dm3 */
{ N_("S1"), 0xd3, 0 },
{ N_("S2"), 0xe3, 0 },
{ N_("S3"), 0xf3, 0 },
{ N_("cS1"), 0xd2, 0 },
{ N_("cS2"), 0xe2, 0 },
{ N_("cS3"), 0xf2, 0 },
/* e.g. 5Ds */
{ N_("M1"), 0x53, 0 },
{ N_("M2"), 0x63, 0 },
{ N_("cM1"), 0x52, 0 },
{ N_("cM2"), 0x62, 0 },
/* user/custom compression, e.g. 1DXm2 */
{ N_("L"), 0x01, 0 },
{ N_("M1"), 0x51, 0 },
{ N_("M2"), 0x61, 0 },
{ N_("S"), 0x21, 0 },
/* user/custom compression, e.g. R5m2 */
{ N_("L"), 0x00, 0 },
{ N_("M"), 0x10, 0 },
{ N_("S1"), 0xe0, 0 },
{ N_("S2"), 0xd0, 0 },
};

static const char*
_single_EOS_ImageFormat_name(uint8_t val)
{
for (unsigned i = 0; i < ARRAYSIZE(canon_eos_single_ImageFormats); ++i)
if (canon_eos_single_ImageFormats[i].value == val)
return canon_eos_single_ImageFormats[i].label;
static char buf[5];
sprintf (buf, "0x%02x", val);
return buf;
}

static int
_get_Canon_EOS_ImageFormat2(CONFIG_GET_ARGS)
{
gp_widget_new (GP_WIDGET_RADIO, _(menu->label), widget);
gp_widget_set_name (*widget, menu->name);

for (unsigned i = 0; i < dpd->FORM.Enum.NumberOfValues; i++) {
uint16_t val = dpd->FORM.Enum.SupportedValue[i].u16;
uint8_t val1 = (val >> 8) & 0xFF;
uint8_t val2 = (val >> 0) & 0xFF;

const char* name1 = _single_EOS_ImageFormat_name(val1);
const char* name2 = _single_EOS_ImageFormat_name(val2);

char buf[12] = { 0 };
strcpy (buf, name1);
if (val2 != 0xFF)
sprintf (buf + strlen(buf), " + %s", name2);

gp_widget_add_choice (*widget, buf);

if (val == dpd->CurrentValue.u16)
gp_widget_set_value (*widget, buf);
}

return GP_OK;
}

static uint8_t
_single_EOS_ImageFormat_value(const char *name, size_t n, PTPDevicePropDesc *dpd)
{
for (unsigned i = 0; i < ARRAYSIZE(canon_eos_single_ImageFormats); ++i)
if (strncmp (canon_eos_single_ImageFormats[i].label, name, n) == 0)
for (unsigned j = 0; j < dpd->FORM.Enum.NumberOfValues; ++j)
if (dpd->FORM.Enum.SupportedValue[j].u16 >> 8 == canon_eos_single_ImageFormats[i].value)
return canon_eos_single_ImageFormats[i].value;
return 0xFF;
}

static int
_put_Canon_EOS_ImageFormat2(CONFIG_PUT_ARGS) {
const char* label;
gp_widget_get_value(widget, &label);

const char *sep = strstr(label, " + ");
size_t n = sep ? (size_t)(sep - label) : strlen(label);

uint8_t val1 = _single_EOS_ImageFormat_value(label, n, dpd);
uint8_t val2 = sep ? _single_EOS_ImageFormat_value(sep + 3, -1, dpd) : 0xFF;
if (val1 == 0xFF) {
GP_LOG_E("could not find '%s' in list of supported image formats", label);
return GP_ERROR_BAD_PARAMETERS;
}
propval->u16 = val1 << 8 | val2;
GP_LOG_D("FOUND right value for %s in the enumeration at val %04x", label, propval->u16);

return GP_OK;
}

static struct deviceproptableu16 canon_eos_aeb[] = {
{ N_("off"), 0x0000, 0 },
{ "+/- 1/3", 0x0003, 0 },
Expand Down Expand Up @@ -10941,6 +11047,7 @@ static struct submenu image_settings_menu[] = {
{ N_("Image Format"), "imageformat", PTP_DPC_CANON_EOS_ImageFormat, PTP_VENDOR_CANON, PTP_DTC_UINT16, _get_Canon_EOS_ImageFormat, _put_Canon_EOS_ImageFormat },
{ N_("Image Format SD"), "imageformatsd", PTP_DPC_CANON_EOS_ImageFormatSD, PTP_VENDOR_CANON, PTP_DTC_UINT16, _get_Canon_EOS_ImageFormat, _put_Canon_EOS_ImageFormat },
{ N_("Image Format CF"), "imageformatcf", PTP_DPC_CANON_EOS_ImageFormatCF, PTP_VENDOR_CANON, PTP_DTC_UINT16, _get_Canon_EOS_ImageFormat, _put_Canon_EOS_ImageFormat },
{ N_("Image Format 2"), "imageformat2", PTP_DPC_CANON_EOS_ImageFormat, PTP_VENDOR_CANON, PTP_DTC_UINT16, _get_Canon_EOS_ImageFormat2, _put_Canon_EOS_ImageFormat2 },
{ N_("Image Format"), "imageformat", PTP_DPC_FUJI_Quality, PTP_VENDOR_FUJI, PTP_DTC_UINT16, _get_Fuji_ImageFormat, _put_Fuji_ImageFormat },
{ N_("Image Format"), "imageformat", 0, PTP_VENDOR_PANASONIC,PTP_DTC_UINT16, _get_Panasonic_ImageFormat, _put_Panasonic_ImageFormat },
{ N_("Image Format Ext HD"), "imageformatexthd", PTP_DPC_CANON_EOS_ImageFormatExtHD, PTP_VENDOR_CANON, PTP_DTC_UINT16, _get_Canon_EOS_ImageFormat, _put_Canon_EOS_ImageFormat },
Expand Down
99 changes: 57 additions & 42 deletions camlibs/ptp2/ptp-pack.c
Original file line number Diff line number Diff line change
Expand Up @@ -1407,66 +1407,77 @@ static inline uint16_t
ptp_unpack_EOS_ImageFormat (PTPParams* params, const unsigned char** data )
{
/*
EOS ImageFormat entries (of at least the 5DM2 and the 400D) look like this:
uint32: number of entries / generated files (1 or 2)
uint32: size of this entry in bytes (most likely always 0x10)
uint32: image type (1 == JPG, 6 == RAW)
uint32: image size (0 == Large, 1 == Medium, 2 == Small, 0xe == S1, 0xf == S2, 0x10 == S3)
uint32: image compression (2 == Standard/JPG, 3 == Fine/JPG, 4 == Lossles/RAW)
If the number of entries is 2 the last 4 uint32 repeat.

example:
0: 0x 1
1: 0x 10
2: 0x 6
3: 0x 1
4: 0x 4
EOS ImageFormat entries look are a sequence of u32 values:
0: number of entries / generated files (1 or 2)
1: size of this entry in bytes (most likely always 0x10 = 4 x u32)
2: image type:
1 == JPG
6 == RAW
3: image size:
0 == L
1 == M
2 == S
5 == M1 (e.g. 5Ds)
6 == M2
e == S1 (e.g. 5Dm3)
f == S2
10 == S3
4: image compression:
0 == user: JPG (e.g. 1DX, R5m2)
1 == ???: JPG (e.g. 1DXm2, 1DXm3)
2 == coarse: JPG (all)
3 == fine: JPG/cRAW (all)
4 == lossless: RAW (all)

If the number of entries is 2 the values 1-4 repeat

example (cRAW + coarse S1 JPEG): 2 10 6 0 3 10 1 e 2

The idea is to simply 'condense' these values to just one uint16 to be able to conveniently
use the available enumeration facilities (look-up table). The image size and compression
values used to fully describe the image format, but at least since EOS M50 (with cRAW)
it is no longer true - we need to store RAW flag (8).
Hence we generate a uint16 with the four nibles set as follows:
use the available enumeration facilities (look-up table).
Hence we generate a u16 value with the four nibles set as follows:

entry 1 size | entry 1 compression & RAW flag | entry 2 size | entry 2 compression & RAW flag.
entry 1 size | entry 1 type + compression | entry 2 size | entry 2 type + compression.

The above example would result in the value 0x1400.
* The S3 value (0xf) would overflow the nible, hence we decrease all S1,S2,S3 values by 1.
* The to encode the type RAW, we set the 4th bit in the compression nible to 1 (|= 8).
* To distinguish an "empty" second entry from the "custom L JPEG", we set it to 0xff.

The EOS 5D Mark III (and possibly other high-end EOS as well) added the extra fancy S1, S2
and S3 JPEG options. S1 replaces the old Small. -1 the S1/S2/S3 to prevent the 0x10 overflow.
*/
The above example would result in the value 0x0bd2.
*/

const unsigned char* d = *data;
uint32_t n = dtoh32a( d );
const uint8_t* d = *data;
uint32_t offset = 0;
uint32_t n = dtoh32o (d, offset);
uint32_t l, t1, s1, c1, t2 = 0, s2 = 0, c2 = 0;

if (n != 1 && n !=2) {
ptp_debug (params, "parsing EOS ImageFormat property failed (n != 1 && n != 2: %d)", n);
return 0;
}

l = dtoh32a( d+=4 );
l = dtoh32o (d, offset);
if (l != 0x10) {
ptp_debug (params, "parsing EOS ImageFormat property failed (l != 0x10: 0x%x)", l);
return 0;
}

t1 = dtoh32a( d+=4 );
s1 = dtoh32a( d+=4 );
c1 = dtoh32a( d+=4 );
t1 = dtoh32o (d, offset);
s1 = dtoh32o (d, offset);
c1 = dtoh32o (d, offset);

if (n == 2) {
l = dtoh32a( d+=4 );
l = dtoh32o (d, offset);
if (l != 0x10) {
ptp_debug (params, "parsing EOS ImageFormat property failed (l != 0x10: 0x%x)", l);
return 0;
}
t2 = dtoh32a( d+=4 );
s2 = dtoh32a( d+=4 );
c2 = dtoh32a( d+=4 );
t2 = dtoh32o (d, offset);
s2 = dtoh32o (d, offset);
c2 = dtoh32o (d, offset);
}

*data = (unsigned char*) d+4;
*data += offset;

/* deal with S1/S2/S3 JPEG sizes, see above. */
if( s1 >= 0xe )
Expand All @@ -1478,31 +1489,35 @@ ptp_unpack_EOS_ImageFormat (PTPParams* params, const unsigned char** data )
c1 |= (t1 == 6) ? 8 : 0;
c2 |= (t2 == 6) ? 8 : 0;

if (s2 == 0 && c2 == 0)
s2 = c2 = 0xF;

return ((s1 & 0xF) << 12) | ((c1 & 0xF) << 8) | ((s2 & 0xF) << 4) | ((c2 & 0xF) << 0);
}

static inline uint32_t
ptp_pack_EOS_ImageFormat (PTPParams* params, unsigned char* data, uint16_t value)
{
uint32_t n = (value & 0xFF) ? 2 : 1;
uint32_t n = (value & 0xFF) == 0xFF ? 1 : 2;
uint32_t s = 4 + 0x10 * n;

if( !data )
return s;

#define PACK_5DM3_SMALL_JPEG_SIZE( X ) (X) >= 0xd ? (X)+1 : (X)
#define PACK_EOS_S123_JPEG_SIZE( X ) (X) >= 0xd ? (X)+1 : (X)

htod32a(data+=0, n);

htod32a(data+=4, 0x10);
htod32a(data+=4, (((value >> 8) & 0xF) >> 3) ? 6 : 1);
htod32a(data+=4, PACK_5DM3_SMALL_JPEG_SIZE((value >> 12) & 0xF));
htod32a(data+=4, ((value >> 8) & 0xF) & ~8);
htod32a(data+=4, value & 0x0800 ? 6 : 1);
htod32a(data+=4, PACK_EOS_S123_JPEG_SIZE((value >> 12) & 0xF));
htod32a(data+=4, (value >> 8) & 0x7);

if (n==2) {
htod32a(data+=4, 0x10);
htod32a(data+=4, (((value >> 0) & 0xF) >> 3) ? 6 : 1);
htod32a(data+=4, PACK_5DM3_SMALL_JPEG_SIZE((value >> 4) & 0xF));
htod32a(data+=4, ((value >> 0) & 0xF) & ~8);
htod32a(data+=4, value & 0x08 ? 6 : 1);
htod32a(data+=4, PACK_EOS_S123_JPEG_SIZE((value >> 4) & 0xF));
htod32a(data+=4, (value >> 0) & 0x7);
}

#undef PACK_5DM3_SMALL_JPEG_SIZE
Expand Down
2 changes: 2 additions & 0 deletions camlibs/ptp2/ptp-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ inline static int log_on_ptp_error_helper( int _r, const char* _func, const char
}\
} while (0)

#define ARRAYSIZE(ARRAY) (sizeof(ARRAY) / sizeof(ARRAY[0]))

static inline int
is_canon_eos_m(PTPParams *params) {
if (params->deviceinfo.VendorExtensionID != PTP_VENDOR_CANON) return 0;
Expand Down
Loading