ICC-Profile

 view release on metacpan or  search on metacpan

lib/ICC/Profile/vcgt.pm  view on Meta::CPAN

		
		# transform input vector and copy to output
		@{$out} = _transform($self, $dir, @{$in});
		
	} else {
		
		# for each input element
		for my $i (0 .. $#{$in}) {
			
			# if an array reference
			if (ref($in->[$i]) eq 'ARRAY') {
				
				# transform next level
				_crawl($self, $dir, $in->[$i], $out->[$i] = []);
				
			} else {
				
				# error
				croak('invalid transform input');
				
			}
			
		}
		
	}
	
}

# transform input value array (vector)
# direction: 0 - normal, 1 - inverse
# parameters: (object_reference, direction, input_value_array)
# returns: (output_value_array)
sub _transform {

	# get parameters
	my ($self, $dir, @in) = @_;

	# local variables
	my (@out);

	# verify inputs are all scalars
	(@in == grep {! ref()} @in) || croak('invalid transform input');

	# verify number of inputs equals number of channels
	(@in == @{$self->[1]}) || croak('wrong number of input values');

	# for each channel
	for my $i (0 .. $#{$self->[1]}) {
		
		# if 'curv' object
		if (UNIVERSAL::isa($self->[1][$i], 'ICC::Profile::curv')) {
			
			# transform using 'curv' method
			$out[$i] = $self->[1][$i]->_transform($dir, $in[$i]);
			
		} else {
			
			# if normal direction
			if ($dir == 0) {
				
				# forward transform using formula (out = min + (max - min) * input^gamma)
				$out[$i] = $self->[1][$i][1] + ($self->[1][$i][2] - $self->[1][$i][1]) * $in[$i]**$self->[1][$i][0];
				
			} else {
				
				# reverse transform using formula (out = ((input - min)/(max - min))^(1/gamma))
				$out[$i] = (($in[$i] - $self->[1][$i][1])/($self->[1][$i][2] - $self->[1][$i][1]))**(1/$self->[1][$i][0]);
				
			}
			
		}
		
	}

	# return output array
	return(@out);

}

# read vcgt tag from ICC profile
# parameters: (ref_to_object, ref_to_parent_object, file_handle, ref_to_tag_table_entry)
sub _readICCvcgt {

	# get parameters
	my ($self, $parent, $fh, $tag) = @_;

	# local variables
	my ($tagType, $buf, $channels, $count, $size, @table);

	# save tag signature
	$self->[0]{'signature'} = $tag->[0];

	# seek start of tag
	seek($fh, $tag->[1], 0);

	# read first 12 bytes
	read($fh, $buf, 12);

	# unpack tagType (0 = cmVideoCardGammaTableType, 1 = cmVideoCardGammaFormulaType)
	$tagType = unpack('x8 N', $buf);

	# if table
	if ($tagType == 0) {
		
		# read 6 bytes
		read($fh, $buf, 6);
		
		# unpack channels, entryCount, entrySize
		($channels, $count, $size) = unpack('n3', $buf);
		
		# save entrySize in header hash
		$self->[0]{'entrySize'} = $size;
		
		# for each channel (gray or RGB)
		for my $i (0 .. $channels - 1) {
			
			# read table data
			read($fh, $buf, $count * $size);
			
			# if 8-bit
			if ($size == 1) {
				
				# unpack table
				@table = unpack('C*', $buf);
				
				# save as 'curv' object
				$self->[1][$i] = ICC::Profile::curv->new([map {$_/255} @table]);
				
			# else 16-bit
			} else {
				
				# unpack table
				@table = unpack('n*', $buf);
				
				# save as 'curv' object
				$self->[1][$i] = ICC::Profile::curv->new([map {$_/65535} @table]);
				
			}
			
		}
		
	# if formula
	} elsif ($tagType == 1) {
		
		# for each RGB
		for my $i (0 .. 2) {
			
			# read 12 bytes
			read($fh, $buf, 12);
			
			# unpack gamma, min, max (s15Fixed16Number values)
			$self->[1][$i] = [ICC::Shared::s15f162v(unpack('N3', $buf))];
			
		}
		
	} else {
		
		# error
		croak('invalid \'vcgt\' tagType');
		
	}
	
}

# write vcgt tag to ICC profile
# parameters: (ref_to_object, ref_to_parent_object, file_handle, ref_to_tag_table_entry)
sub _writeICCvcgt {

	# get parameters
	my ($self, $parent, $fh, $tag) = @_;

	# local variables
	my ($tagType, $channels, $count, $size, @table, $gamma);

	# seek start of tag
	seek($fh, $tag->[1], 0);

	# determine tagType (0 = cmVideoCardGammaTableType, 1 = cmVideoCardGammaFormulaType)
	$tagType = (UNIVERSAL::isa($self->[1][0], 'ICC::Profile::curv') && @{$self->[1][0]->array()} > 1) ? 0 : 1;

	# write signature and tagType
	print $fh pack('a4 x4 N', 'vcgt', $tagType);

	# if table
	if ($tagType == 0) {
		
		# get channels
		$channels = @{$self->[1]};
		
		# get entryCount
		$count = @{$self->[1][0]->array()};
		
		# if entrySize is 8-bit
		$size = (defined($self->[0]{'entrySize'}) && $self->[0]{'entrySize'} == 1) ? 1 : 2;
		
		# write channels, entryCount, entrySize
		print $fh pack('n3', $channels, $count, $size);
		
		# for each channel (gray or RGB)
		for my $i (0 .. $channels - 1) {
			
			# if 8-bit
			if ($size == 1) {
				
				# write table limiting values, converting to 8-bit, adding 0.5 to round
				print $fh pack('C*', map {$_ < 0 ? 0 : ($_ > 1 ? 255 : $_ * 255 + 0.5)} @{$self->[1][$i]->array()});
				
			# else 16-bit
			} else {
				
				# write table limiting values, converting to 16-bit, adding 0.5 to round
				print $fh pack('n*', map {$_ < 0 ? 0 : ($_ > 1 ? 65535 : $_ * 65535 + 0.5)} @{$self->[1][$i]->array()});
				
			}
			
		}
		
	# if formula
	} else {
		
		# if gamma type 'curv' objects
		if (UNIVERSAL::isa($self->[1][0], 'ICC::Profile::curv')) {
			
			# for each RGB
			for my $i (0 .. 2) {
				
				# get 'curv' object index (could be just one 'curv')
				my $j = defined($self->[1][$i]) ? $i : 0;
				
				# get gamma (use 1.0 if undefined)
				$gamma = defined($self->[1][$j]->array->[0]) ? $self->[1][$j]->array->[0] : 1;
				
				# write gamma, min, max (s15Fixed16Number values)
				print $fh pack('N3', ICC::Shared::v2s15f16($gamma, 0, 1));
				
			}
			
		# if numeric array
		} else {
			
			# for each RGB
			for my $i (0 .. 2) {
				
				# write gamma, min, max (s15Fixed16Number values)
				print $fh pack('N3', ICC::Shared::v2s15f16(@{$self->[1][$i]}));
				
			}
			
		}
		
	}
	
}

1;



( run in 0.494 second using v1.01-cache-2.11-cpan-5735350b133 )