# -----------------------------------------------------------------------------

=encoding utf8

=head1 NAME

Quiq::File::Image - Informationen über Bild-Datei

=head1 BASE CLASS

L<Quiq::Hash>

=cut

# -----------------------------------------------------------------------------

package Quiq::File::Image;
use base qw/Quiq::Hash/;

use v5.10;
use strict;
use warnings;

our $VERSION = '1.226';

use Quiq::Hash;
use Encode ();
use Quiq::Path;
use Quiq::Image;
use Quiq::Array;
use Image::ExifTool ();
use Image::Size ();

# -----------------------------------------------------------------------------

=head1 METHODS

=head2 Konstruktor

=head3 new() - Instantiiere Bild-Datei-Objekt

=head4 Synopsis

  $img = $class->new($path);

=head4 Description

Instantiiere ein Bild-Datei-Objekt und liefere eine Referenz auf
dieses Objekt zurück.

=cut

# -----------------------------------------------------------------------------

sub new {
    my ($class,$path) = @_;

    if (!-e Encode::encode('utf-8',$path)) {
        $class->throw(
             'IMAGE-00001: File not found',
             Path => $path,
        );
    }

    # Objekt instantiieren

    return $class->SUPER::new(
        # Eigenschaften
        path => $path,
        filename => undef,
        basename => undef,
        extension => undef,
        width => undef,
        height => undef,
        created => undef,
        type => undef,
        # Properties
        propertyH => Quiq::Hash->new->unlockKeys,
    );
}

# -----------------------------------------------------------------------------

=head2 Eigenschaften

=head3 path() - Datei-Pfad

=head4 Synopsis

  $path = $img->path;

=head4 Description

Liefere den Dateinamen einschl. Pfad der Bild-Datei.

=head3 filename() - Datei-Name ohne Verzeichnisanteil

=head4 Synopsis

  $filename = $img->filename;

=head4 Description

Liefere den Dateinamen ohne Verzeichnisanteil, aber mit Extension,
z.B. '000456.jpg'.

=cut

# -----------------------------------------------------------------------------

sub filename {
    my $self = shift;

    return $self->memoize('filename',sub {
        my ($self,$key) = @_;
        $self->path =~ m|/([^/]+)$|;
        return $1;
    });
}

# -----------------------------------------------------------------------------

=head3 basename() - Datei-Name ohne Verzeichnis und Extension

=head4 Synopsis

  $basename = $img->basename;

=head4 Description

Liefere den Dateinamen ohne Verzeichnis und Extension, z.B. '000456'.

=cut

# -----------------------------------------------------------------------------

sub basename {
    my $self = shift;

    return $self->memoize('basename',sub {
        my ($self,$key) = @_;
        $self->filename =~ m|([^.]+)|;
        return $1;
    });
}

# -----------------------------------------------------------------------------

=head3 extension() - Datei-Extension

=head4 Synopsis

  $ext = $img->extension;

=head4 Description

Liefere die Extentsion der Datei, z.B. 'png'.

=cut

# -----------------------------------------------------------------------------

sub extension {
    my $self = shift;

    return $self->memoize('extension',sub {
        my ($self,$key) = @_;
        return Quiq::Path->extension($self->path);
    });
}

# -----------------------------------------------------------------------------

=head3 mtime() - Letzte Änderung

=head4 Synopsis

  $mtime = $img->mtime;

=head4 Description

Liefere den Zeitpunkt (Unix Epoch), an dem die Bilddatei
das letzte Mal geändert wurde.

=cut

# -----------------------------------------------------------------------------

sub mtime {
    return (stat(shift->path))[9];
}

# -----------------------------------------------------------------------------

=head3 type() - Bild-Typ

=head4 Synopsis

  $type = $img->type;

=head4 Description

Liefere den Datei-Typ des Bildes, z.B. 'jpg'.

=cut

# -----------------------------------------------------------------------------

sub type {
    my $self = shift;

    return $self->memoize('type',sub {
        my ($self,$key) = @_;

        my $type = Quiq::Image->type($self->path);
        if ($type eq 'jpeg') {
            $type = 'jpg';
        }

        return $type;
    });
}

# -----------------------------------------------------------------------------

=head3 width() - Breite

=head4 Synopsis

  $width = $img->width;

=head4 Description

Liefere die Breite des Bildes in Pixeln, z.B. 1920.

=cut

# -----------------------------------------------------------------------------

sub width {
    my $self = shift;

    return $self->memoize('width',sub {
        my ($self,$key) = @_;
        $self->analyzeFile;
        return $self->get($key);
    });
}

# -----------------------------------------------------------------------------

=head3 height() - Höhe

=head4 Synopsis

  $height = $img->height;

=head4 Description

Liefere die Höhe des Bildes in Pixeln, z.B. 1080.

=cut

# -----------------------------------------------------------------------------

sub height {
    my $self = shift;

    return $self->memoize('height',sub {
        my ($self,$key) = @_;
        $self->analyzeFile;
        return $self->get($key);
    });
}

# -----------------------------------------------------------------------------

=head3 created() - Aufnahmezeitpunkt

=head4 Synopsis

  $created = $img->created;

=head4 Description

Liefere den Aufnahmezeitpunkt des Bildes, z.B. '2024-11-11 22:22:06'.

=cut

# -----------------------------------------------------------------------------

sub created {
    my $self = shift;

    return $self->memoize('created',sub {
        my ($self,$key) = @_;
        $self->analyzeFile;
        return $self->get($key);
    });
}

# -----------------------------------------------------------------------------

=head3 size() - Breite und Höhe

=head4 Synopsis

  $size = $img->size;
  ($width,$height) = $img->size;

=head4 Description

Liefere die Breite und Höhe des Bildes in Pixeln. Im Skalarkontext
werden die Breite und Höhe in einem String zusammengefasst geliefert:

  'WIDTHxHEIGHT'

=head4 Examples

List-Kontext:

  $img->size;
  =>
  (1920,1080)

Skalar-Kontext:

  $img->size;
  =>
  '1920x1080'

=cut

# -----------------------------------------------------------------------------

sub size {
    my $self = shift;
    my $width = $self->width;
    my $height = $self->height;
    return wantarray? ($width,$height): $width.'x'.$height;
}

# -----------------------------------------------------------------------------

=head3 scaleFactor() - Skalierungsfaktor für andere Breite, Höhe

=head4 Synopsis

  $scale = $img->scaleFactor($width,$height);

=head4 Description

Liefere den Skalierungsfaktor, wenn das Bild auf die Breite $width
und die Höhe $height skaliert werden soll. Werden nicht-proportionale
Werte für $width und $height angegeben, dass also für die Breite
und die Höhe unterschiedliche Skalierungsfakoren berechnet werden,
liefere von beiden den kleineren Wert.

=head4 Example

Das Bild hat die Größe 249 x 249 und soll skaliert werden auf
die Größe 83 x 83:

  $scale = $img->scaleFactor(83,83);
  # 0.333333333333333

=cut

# -----------------------------------------------------------------------------

sub scaleFactor {
    my ($self,$width,$height) = @_;
    return Quiq::Array->min([$width/$self->width,$height/$self->height]);
}

# -----------------------------------------------------------------------------

=head3 aspectRatio() - Seitenverhältnis

=head4 Synopsis

  $aspectRatio = $img->aspectRatio;

=head4 Description

Liefere das Seitenverhältnis des Bildes, z.B. '16:9'.

=cut

# -----------------------------------------------------------------------------

sub aspectRatio {
    my $self = shift;
    return Quiq::Image->aspectRatio($self->width,$self->height);
}

# -----------------------------------------------------------------------------

=head2 Properties

=head3 property() - Liefere Property-Hash

=head4 Synopsis

  $h = $img->property;
  $h = $img->property(\%hash);

=head4 Description

Liefere eine Referenz auf den Property-Hash des Bildes. Der
Property-Hash speichert zusätzliche Eigenschaften des
Bild-Datei-Objektes, die z.B. im Zuge einer Bild-Bearbeitung
verwendet werden.

Der Property-Hash ist ein Quiq::Hash-Objekt, dessen Schlüssel
nicht gelockt sind. Nach der Objekt-Instantiierung ist der
Property-Hash leer.

=head4 Examples

Setze Eigenschaft:

  $img->property->set(sizeFill=>[1440,1080]);

Eigenschaft abfragen:

  ($width,$height) = $img->property->getArray('sizeFill');

=cut

# -----------------------------------------------------------------------------

sub property {
    my $self = shift;
    # @_: \%hash

    if (@_) {
        return $self->{'propertyH'} = shift;
    }

    return $self->{'propertyH'};
}

# -----------------------------------------------------------------------------

=head2 Interne Methoden

=head3 analyzeFile() - Analysiere Bild-Datei

=head4 Synopsis

  $img->analyzeFile;

=head4 Description

Analysiere die Bild-Datei und weise die ermittelten
Eigenschaften an die Attribute des Objektes zu.

=cut

# -----------------------------------------------------------------------------

our $xft = Image::ExifTool->new; # nur einmal instantiieren

sub analyzeFile {
    my $self = shift;

    if (!defined $self->get('width')) {
        my ($width,$height) = Image::Size::imgsize($self->path);
        $self->set(width=>$width);
        $self->set(height=>$height);
        my $h = $xft->ImageInfo($self->path);
        my $created = $h->{'CreateDate'} || $h->{'DateTimeOriginal'} || '';
        if ($created) {
            my @arr = split /[^\d]+/,$created;
            $created = sprintf '%04d-%02d-%02d %02d:%02d:%02d',@arr;
        }
        $self->set(created=>$created);
    }

    return;
}

# -----------------------------------------------------------------------------

=head1 VERSION

1.226

=head1 AUTHOR

Frank Seitz, L<http://fseitz.de/>

=head1 COPYRIGHT

Copyright (C) 2025 Frank Seitz

=head1 LICENSE

This code is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut

# -----------------------------------------------------------------------------

1;

# eof
