The interface you get there is quite low level, mapping rather directly to GDI+ (i.e. a C++ interface).
If we had more time we could clearly "raise" the interface to a higher level.
Let us start by considering what kinds of data we are dealing with, by considering a couple of the properties.
The first property 315 (= 0x13B) is
- PropertyTagArtist
- Null-terminated character string that specifies the name of the person who created the image.
- Tag: 0x013B
- Type: PropertyTagTypeASCII
- Count: Length of the string including the NULL terminator
So for PropertyTagTypeASCII we are dealing with a
string8 value.
Property 40093 (= 0x9C9D) is not in that list, but the principle will follow the one for 20626 (= 0x5091):
- PropertyTagArtist
- Chrominance table. The luminance table and the chrominance table are used to control JPEG quality. A valid luminance or chrominance table has 64 entries of type PropertyTagTypeShort. If an image has either a luminance table or a chrominance table, then it must have both tables.
- Tag: 0x5091
- Type: PropertyTagTypeShort
- Count: 64
So here we are dealing with an array consisting of 64
shorts; which corresponds to
core::unsigned16. 64 16 bit unsigned is consistent with the byte size 128 that you get.
Lets us take one more:
- PropertyTagGpsLongitude
- Longitude. Longitude is expressed as three rational values giving the degrees, minutes, and seconds respectively. When degrees, minutes and seconds are expressed, the format is ddd/1, mm/1, ss/1. When degrees and minutes are used and, for example, fractions of minutes are given up to two decimal places, the format is ddd/1, mmmm/100, 0/1.
- Tag: 0x0004
- Type: PropertyTagTypeRational
- Count: 3
So each rational consists of two unsigned which are the nominator and denominator of a fraction. For a GPS Longitude there are three of them. So the byte size in this case will be 3 (rationals) * 2 (unsigned) * 4 bytes = 24 Bytes.
There is a little "twist" in those cases where there is
one value in the array. For example in
- PropertyTagGpsSpeed
- Speed of the GPS receiver movement.
- Tag: 0x000D
- Type: PropertyTagTypeRational
- Count: 1
In such cases it is not really an array with one value, it is just the value.
The PropertyTagTypeByte is most likely not really a lot of numbers, but just "some data" (a thumbnail for example), but if there is only one then it is quitelikely a number.
The PropertyTagTypeUndefined is in any case just "some data", even when there is only one byte.
All in all however there ar quite a number of different kinds of data.
And the first question is: What do you what to have in Prolog?
If you want to represent it as genuine Prolog data then you should have a functor domain covering all the different cases.
Code: Select all
domains
propertyValue =
byte(unsigned8 Value);
byteArray(binary Value);
string8(string8 Value);
rational(tuple{unsigned Nominator, unsigned Denominator});
rationalArray(tuple{unsigned Nominator, unsigned Denominator}* List);
rationlS(tuple{integer Nominator, integer Denominator});
...
There are many choices to make:
- I have chosen that arrays should be represented as lists.
- I have chosen to have separate "one value" cases, but they could also just be lists with one element.
- ...
It is somewhat simpler to convert data to strings. Because a string can represent any kind of data, so in that case we don't need all the mentioned functors etc.
Here is an example of that:
Code: Select all
class predicates
writeProperties : (string ImageFile).
clauses
writeProperties(ImageFile) :-
Image = image::createFromFile(ImageFile),
Image:getPropertySize(TotalBufferSize, NumberOfProperties),
ALL = Image:getAllPropertyItems(TotalBufferSize, NumberOfProperties),
foreach propertyItem(Id, Size, Type, Pointer) in ALL do
writef("%: %\n", gdiplus::getPropertyName(Id), getValueString(Size, Type, Pointer))
end foreach.
domains
fraction{Type} =
fraction(Type Nominator, Type Denominator)
[presenter(presenter_fraction)].
class predicates
getValueString : (byteCount Size, propertyTagType Type, pointer Pointer) -> string ValueString.
clauses
getValueString(_Size, propertyTagTypeASCII, Pointer) = ValueString :-
!,
ValueString = string::format("%", uncheckedConvert(string8, Pointer)).
getValueString(1, propertyTagTypeByte, Pointer) = ValueString :-
!,
ValueString = getValueString_value(1, uncheckedConvert(memory::pointerTo{unsigned8}, Pointer)).
getValueString(Size, propertyTagTypeShort, Pointer) = ValueString :-
!,
ValueString = getValueString_value(Size, uncheckedConvert(memory::pointerTo{unsigned16}, Pointer)).
getValueString(Size, propertyTagTypeLong, Pointer) = ValueString :-
!,
ValueString = getValueString_value(Size, uncheckedConvert(memory::pointerTo{unsigned}, Pointer)).
getValueString(Size, propertyTagTypeSLong, Pointer) = ValueString :-
!,
ValueString = getValueString_value(Size, uncheckedConvert(memory::pointerTo{integer}, Pointer)).
getValueString(Size, propertyTagTypeRational, Pointer) = ValueString :-
!,
ValueString = getValueString_struct(Size, uncheckedConvert(fraction{unsigned}, Pointer)).
getValueString(Size, propertyTagTypeSRational, Pointer) = ValueString :-
!,
ValueString = getValueString_struct(Size, uncheckedConvert(fraction{integer}, Pointer)).
getValueString(Size, Type, Pointer) = string::format("% (%)", binary::createAtomicFromPointer(Pointer, Size), Type).
class predicates
getValueString_value : (byteCount Size, memory::pointerTo{Type} Pointer) -> string ValueString.
clauses
getValueString_value(Size, PointerTo) = list_string(buildList_value(Size, PointerTo)).
class predicates
getValueString_struct : (byteCount Size, Struct Pointer) -> string ValueString.
clauses
getValueString_struct(Size, Pointer) = list_string(buildList_struct(Size, Pointer)).
class predicates
list_string : (Type* List) -> string ValueString.
clauses
list_string([Elem]) = string::present(Elem) :-
!.
list_string(List) = string::present(List).
class predicates
buildList_value : (byteCount Size, memory::pointerTo{Type} PointerTo) -> Type* List.
clauses
buildList_value(0, _PointerTo) = [] :-
!.
buildList_value(Size, PointerTo) = [V | VL] :-
V = memory::get(PointerTo),
SizeElem = sizeof(V),
Next = memory::uncheckedFromPointer(memory::pointerAddLite(uncheckedConvert(pointer, PointerTo), SizeElem)),
VL = buildList_value(Size - SizeElem, Next).
class predicates
buildList_struct : (byteCount Size, Struct Pointer) -> Struct* List.
clauses
buildList_struct(0, _Pointer) = [] :-
!.
buildList_struct(Size, Pointer) = [V | VL] :-
V = Pointer,
SizeElem = sizeof(V),
Next = memory::uncheckedFromPointer(memory::pointerAddLite(uncheckedConvert(pointer, Pointer), SizeElem)),
VL = buildList_struct(Size - SizeElem, Next).
class predicates
presenter_fraction : presenter::presenter{fraction{Type}}.
clauses
presenter_fraction(fraction(Nominator, Denominator)) =
presenter::mkPresenter_fixed(presenter::mkFP_term("Nominator", Nominator), presenter::fp_string("/"),
presenter::mkFP_term("Denominator", Denominator)).
The code contains some pointer arithmetic and memory manipulation, which require "deep" understanding of representation.
I have added a complete project which also contains an updade of the
gdiplus class, notably the predicate
getPropertyName which makes it all much nicer.