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.
You do not have the required permissions to view the files attached to this post.