#------------------------------------------------------------------------------
# File:         convert_regions.config
#
# Description:  User-defined Composite tag definitions to allow conversion of
#               face regions between Microsoft Windows Live Photo Gallery (WLPG),
#               Metadata Working Group (MWG), and IPTC Extension formats
#
# Usage:     1) Convert from MP WLPG or IPTC regions to MWG regions:
#
#               exiftool -config convert_regions.config "-RegionInfo<MPRegion2MWGRegion" FILE
#
#            2) Convert from MWG or IPTC to MP WLPG regions:
#
#               exiftool -config convert_regions.config "-RegionInfoMP<MWGRegion2MPRegion" FILE
#
#            3) Convert from MWG or MP WLPG to IPTC regions:
#
#               exiftool -config convert_regions.config "-ImageRegion<MWGRegion2IPTCRegion" FILE
#
# Requires:     ExifTool version 11.74 or later
#
# Revisions:    2012/12/27 - P. Harvey Created
#               2013/02/20 - PH Don't add ignored MP faces
#               2017/02/13 - PH Handle MP regions without Rectangle or Name entries
#               2019/10/26 - PH Added support for the new IPTC ImageRegion
#               2021/05-27 - PH Changed a few tag names and added shortcuts for
#                            backward compatibility
#
# References:   http://www.metadataworkinggroup.org/specs/
#------------------------------------------------------------------------------

%Image::ExifTool::UserDefined = (

    'Image::ExifTool::Composite' => {

        # create an MWG RegionInfo structure from a Microsoft RegionInfoMP structure
        MPRegion2MWGRegion => {
            Require => {
                0 => 'RegionInfoMP',
                1 => 'ImageWidth',
                2 => 'ImageHeight',
            },
            ValueConv => q{
                my ($rgn, @newRgns);
                foreach $rgn (@{$val[0]{Regions}}) {
                    my $name = $$rgn{PersonDisplayName};
                    next unless $$rgn{Rectangle} or defined $name;
                    my %newRgn = ( Type => 'Face' );
                    if (defined $name) {
                        # don't add ignored faces
                        next if $name eq 'ffffffffffffffff';
                        $newRgn{Name} = $name;
                    }
                    if ($$rgn{Rectangle}) {
                        my @rect = split /\s*,\s*/, $$rgn{Rectangle};
                        $newRgn{Area} = {
                            X => $rect[0] + $rect[2]/2,
                            Y => $rect[1] + $rect[3]/2,
                            W => $rect[2],
                            H => $rect[3],
                            Unit => 'normalized',
                        } if @rect == 4;
                    }
                    push @newRgns, \%newRgn;
                }
                return {
                    AppliedToDimensions => { W => $val[1], H => $val[2], Unit => 'pixel' },
                    RegionList => \@newRgns,
                };
            },
        },

        # create an MWG RegionInfo structure from an IPTC ImageRegion list
        IPTCRegion2MWGRegion => {
            Name => 'MPRegion2MWGRegion',
            Require => {
                0 => 'ImageRegion',
                1 => 'ImageWidth',
                2 => 'ImageHeight',
            },
            ValueConv => q{
                my ($rgn, @newRgns);
                my $rgns = ref $val[0] eq 'ARRAY' ? $val[0] : [ $val[0] ];
                foreach $rgn (@$rgns) {
                    my %newRgn = ( Type => 'Face' );
                    if ($$rgn{RegionBoundary} and $$rgn{RegionBoundary}{RbShape} eq 'rectangle') {
                        my @rect = @{$$rgn{RegionBoundary}}{'RbX','RbY','RbW','RbH'};
                        if ($$rgn{RegionBoundary}{RbUnit} eq 'pixel') {
                            $rect[0] /= $val[1],  $rect[2] /= $val[1];
                            $rect[1] /= $val[2];  $rect[3] /= $val[2];
                        }
                        $newRgn{Area} = {
                            X => $rect[0] + $rect[2]/2,
                            Y => $rect[1] + $rect[3]/2,
                            W => $rect[2],
                            H => $rect[3],
                            Unit => 'normalized',
                        };
                    } else {
                        next unless defined $$rgn{Name};
                    }
                    $newRgn{Name} = $$rgn{Name} if defined $$rgn{Name};
                    push @newRgns, \%newRgn;
                }
                return {
                    AppliedToDimensions => { W => $val[1], H => $val[2], Unit => 'pixel' },
                    RegionList => \@newRgns,
                };
            },
        },

        # create a Microsoft RegionInfoMP structure from an MWG RegionInfo structure
        MWGRegion2MPRegion => {
            Require => 'RegionInfo',
            ValueConv => q{
                my ($rgn, @newRgns);
                foreach $rgn (@{$val[0]{RegionList}}) {
                    next unless $$rgn{Area} or defined $$rgn{Name};
                    my %newRgn;
                    if ($$rgn{Area}) {
                        my @rect = @{$$rgn{Area}}{'X','Y','W','H'};
                        $rect[0] -= $rect[2]/2;
                        $rect[1] -= $rect[3]/2;
                        $newRgn{Rectangle} = join(', ', @rect);
                    }
                    $newRgn{PersonDisplayName} = $$rgn{Name} if defined $$rgn{Name};
                    push @newRgns, \%newRgn;
                }
                return { Regions => \@newRgns };
            },
        },

        # create a Microsoft RegionInfoMP structure from an IPTC ImageRegion list
        IPTCRegion2MPRegion => {
            Name => 'MWGRegion2MPRegion',
            Require => {
                0 => 'ImageRegion',
                1 => 'ImageWidth',
                2 => 'ImageHeight',
            },
            ValueConv => q{
                my ($rgn, @newRgns);
                my $rgns = ref $val[0] eq 'ARRAY' ? $val[0] : [ $val[0] ];
                foreach $rgn (@$rgns) {
                    my %newRgn;
                    if ($$rgn{RegionBoundary} and $$rgn{RegionBoundary}{RbShape} eq 'rectangle') {
                        my @rect = @{$$rgn{RegionBoundary}}{'RbX','RbY','RbW','RbH'};
                        if ($$rgn{RegionBoundary}{RbUnit} eq 'pixel') {
                            $rect[0] /= $val[1],  $rect[2] /= $val[1];
                            $rect[1] /= $val[2];  $rect[3] /= $val[2];
                        }
                        $newRgn{Rectangle} = join(', ', @rect);
                    } else {
                        next unless defined $$rgn{Name};
                    }
                    $newRgn{PersonDisplayName} = $$rgn{Name} if defined $$rgn{Name};
                    push @newRgns, \%newRgn;
                }
                return { Regions => \@newRgns };
            },
        },

        # create an IPTC ImageRegion list from an MWG RegionInfo structure
        MWGRegion2IPTCRegion => {
            Require => 'RegionInfo',
            ValueConv => q{
                my ($rgn, @newRgns);
                foreach $rgn (@{$val[0]{RegionList}}) {
                    next unless $$rgn{Area} or defined $$rgn{Name};
                    my %newRgn;
                    if ($$rgn{Area}) {
                        my @rect = @{$$rgn{Area}}{'X','Y','W','H'};
                        $rect[0] -= $rect[2]/2;
                        $rect[1] -= $rect[3]/2;
                        $newRgn{RegionBoundary} = {
                            RbShape => 'rectangle',
                            RbUnit => 'relative',
                            RbX => $rect[0],
                            RbY => $rect[1],
                            RbW => $rect[2],
                            RbH => $rect[3],
                        };
                    }
                    $newRgn{Name} = $$rgn{Name} if defined $$rgn{Name};
                    push @newRgns, \%newRgn;
                }
                return \@newRgns;
            },
        },

        # create an IPTC ImageRegion list from a Microsoft RegionInfoMP structure
        MPRegion2IPTCRegion => {
            Name => 'MWGRegion2IPTCRegion',
            Require => 'RegionInfoMP',
            ValueConv => q{
                my ($rgn, @newRgns);
                foreach $rgn (@{$val[0]{Regions}}) {
                    my $name = $$rgn{PersonDisplayName};
                    next unless $$rgn{Rectangle} or defined $name;
                    my %newRgn;
                    if (defined $name) {
                        # don't add ignored faces
                        next if $name eq 'ffffffffffffffff';
                        $newRgn{Name} = $name;
                    }
                    if ($$rgn{Rectangle}) {
                        my @rect = split /\s*,\s*/, $$rgn{Rectangle};
                        $newRgn{RegionBoundary} = {
                            RbShape => 'rectangle',
                            RbUnit => 'relative',
                            RbX => $rect[0],
                            RbY => $rect[1],
                            RbW => $rect[2],
                            RbH => $rect[3],
                        } if @rect == 4;
                    }
                    push @newRgns, \%newRgn;
                }
                return \@newRgns;
            },
        },
    },
);

%Image::ExifTool::UserDefined::Shortcuts = (
    MyRegion    => 'MPRegion2MWGRegion',
    MyRegion2   => 'IPTCRegion2MWGRegion',
    MyRegionMP  => 'MWGRegion2MPRegion',
    MyRegionMP2 => 'IPTCRegion2MPRegion',
    MyRegionIPTC    => 'MWGRegion2IPTCRegion',
    MyRegionIPTC2   => 'MPRegion2IPTCRegion',
);

1;  #end
