Снова зарылся в дебри и нашел статью о свойствах изображения для GDI+. Меня особенно заинтересовала возможность добавления и чтения текстовой информации. Написал программу, которая еле-еле запустилась, помогли ребята на форуме(load_jpg.pb)
#GDIPLUS_OK = 0
Enumeration
#RotateNoneFlipNone
#Rotate90FlipNone
#Rotate180FlipNone
#Rotate270FlipNone
#RotateNoneFlipX
#Rotate90FlipX
#Rotate180FlipX
#Rotate270FlipX
#RotateNoneFlipY = #Rotate180FlipX
#Rotate90FlipY = #Rotate270FlipX
#Rotate180FlipY = #RotateNoneFlipX
#Rotate270FlipY = #Rotate90FlipX
#RotateNoneFlipXY = #Rotate180FlipNone
#Rotate90FlipXY = #Rotate270FlipNone
#Rotate180FlipXY = #RotateNoneFlipNone
#Rotate270FlipXY = #Rotate90FlipNone
EndEnumeration
Enumeration
#EncoderParameterValueTypeByte = 1
#EncoderParameterValueTypeASCII = 2
#EncoderParameterValueTypeShort = 3
#EncoderParameterValueTypeLong = 4
#EncoderParameterValueTypeRational = 5
#EncoderParameterValueTypeLongRange = 6
#EncoderParameterValueTypeUndefined = 7
#EncoderParameterValueTypeRationalRange = 8
#EncoderParameterValueTypePointer = 9
EndEnumeration
;-Structures.
Structure GdiplusStartupInput
GdiPlusVersion.l
*DebugEventCallback.DebugEventProc
SuppressBackgroundThread.l
SuppressExternalCodecs.l
EndStructure
;The following two structures are used for setting encoder parameters (in our case the 'quality' parameter).
Structure EncoderParameter
guid.GUID
NumberOfValues.l
Type.l
Value.l
EndStructure
Structure EncoderParameters
Count.i
Parameter.EncoderParameter[1]
EndStructure
Structure PropertyItem
id.l; // ID of this property
length.l; // Length of the property value, in bytes
type.u ; // Type of the value, as one of TAG_TYPE_XXX
padding.w
*value
EndStructure
;-Imports.
Import "gdiplus.lib"
GdiplusStartup(token, *input.GdiplusStartupInput, output)
GdiplusShutdown(token)
GdipLoadImageFromFile(*filename, *bitmap)
GdipImageRotateFlip(image, rotateFlipType)
GdipSaveImageToFile(image, filename.p-unicode, *clsidEncoder.CLSID, *encoderParams)
GdipDisposeImage(image)
GdipGetAllPropertyItems(*image, totalBufferSize.l, numProperties.l, ai);ai is *allItems.PropertyItem
GdipGetPropertySize(*image, totalBufferSize.i, numProperties.i)
EndImport
sourceFile$="~photo_2025-02-18_14-38-49.jpg"
;sourceFile$="1584812707-a92e3f716beaa04c7000e8afc3b87b59.png"
input.GdiplusStartupInput\GdiPlusVersion = 1
GdiplusStartup(@token, @input, #Null)
;Was the initialisation successful?
If token
If GdipLoadImageFromFile(@sourceFile$, @image) = #GDIPLUS_OK
totalBufferSize=0
numProperties=0
If GdipGetPropertySize(image, @totalBufferSize, @numProperties) = #GDIPLUS_OK
If totalBufferSize And numProperties
*m=AllocateMemory(totalBufferSize)
rr=gdipGetAllPropertyItems(image,totalBufferSize, numProperties,*m)
; CreateFile(0,"prop.bin")
; WriteData(0,*m,totalBufferSize)
; CloseFile(0)
*pp.PropertyItem=AllocateMemory(SizeOf(PropertyItem))
For i=0 To numProperties-1
CopyMemory(*m+i*SizeOf(PropertyItem),*pp,SizeOf(PropertyItem))
Debug "id="+Hex(*pp\id)+",len="+Str(*pp\length)+",type="+Str(*pp\type);;+",value="+Str(pp\*value)
If *pp\type=1 And *pp\id=$9c9c
; ShowMemoryViewer(*pp\value, *pp\length)
Debug PeekS(*pp\value,*pp\length,#PB_Unicode)
EndIf
;type=1, id=9c9c EXIF comment
If *pp\type=2
Debug PeekS(*pp\value,*pp\length,#PB_Ascii);ShowMemoryViewer(*pp\value, 1024)
EndIf
Next i
FreeMemory(*pp)
FreeMemory(*m)
EndIf
EndIf;If GdipGetPropertySize
GdipDisposeImage(image)
EndIf
; ;Tidy up.
GdiplusShutdown(token)
EndIf
DataSection
;CLSID for the gdi+ jpeg encoder.
clsid_jpeg:
Data.l $557CF401
Data.w $1A04, $11D3
Data.b $9A, $73, $00, $00, $F8, $1E, $F3, $2E
;CLSID for the relevant gdi+ encoder parameter.
clsid_EncoderQuality:
Data.l $1D5BE4B5
Data.w $FA4A, $452D
Data.b $9C, $DD, $5D, $B3, $51, $05, $E7, $EB
EndDataSection
Enumeration
#RotateNoneFlipNone
#Rotate90FlipNone
#Rotate180FlipNone
#Rotate270FlipNone
#RotateNoneFlipX
#Rotate90FlipX
#Rotate180FlipX
#Rotate270FlipX
#RotateNoneFlipY = #Rotate180FlipX
#Rotate90FlipY = #Rotate270FlipX
#Rotate180FlipY = #RotateNoneFlipX
#Rotate270FlipY = #Rotate90FlipX
#RotateNoneFlipXY = #Rotate180FlipNone
#Rotate90FlipXY = #Rotate270FlipNone
#Rotate180FlipXY = #RotateNoneFlipNone
#Rotate270FlipXY = #Rotate90FlipNone
EndEnumeration
Enumeration
#EncoderParameterValueTypeByte = 1
#EncoderParameterValueTypeASCII = 2
#EncoderParameterValueTypeShort = 3
#EncoderParameterValueTypeLong = 4
#EncoderParameterValueTypeRational = 5
#EncoderParameterValueTypeLongRange = 6
#EncoderParameterValueTypeUndefined = 7
#EncoderParameterValueTypeRationalRange = 8
#EncoderParameterValueTypePointer = 9
EndEnumeration
;-Structures.
Structure GdiplusStartupInput
GdiPlusVersion.l
*DebugEventCallback.DebugEventProc
SuppressBackgroundThread.l
SuppressExternalCodecs.l
EndStructure
;The following two structures are used for setting encoder parameters (in our case the 'quality' parameter).
Structure EncoderParameter
guid.GUID
NumberOfValues.l
Type.l
Value.l
EndStructure
Structure EncoderParameters
Count.i
Parameter.EncoderParameter[1]
EndStructure
Structure PropertyItem
id.l; // ID of this property
length.l; // Length of the property value, in bytes
type.u ; // Type of the value, as one of TAG_TYPE_XXX
padding.w
*value
EndStructure
;-Imports.
Import "gdiplus.lib"
GdiplusStartup(token, *input.GdiplusStartupInput, output)
GdiplusShutdown(token)
GdipLoadImageFromFile(*filename, *bitmap)
GdipImageRotateFlip(image, rotateFlipType)
GdipSaveImageToFile(image, filename.p-unicode, *clsidEncoder.CLSID, *encoderParams)
GdipDisposeImage(image)
GdipGetAllPropertyItems(*image, totalBufferSize.l, numProperties.l, ai);ai is *allItems.PropertyItem
GdipGetPropertySize(*image, totalBufferSize.i, numProperties.i)
EndImport
sourceFile$="~photo_2025-02-18_14-38-49.jpg"
;sourceFile$="1584812707-a92e3f716beaa04c7000e8afc3b87b59.png"
input.GdiplusStartupInput\GdiPlusVersion = 1
GdiplusStartup(@token, @input, #Null)
;Was the initialisation successful?
If token
If GdipLoadImageFromFile(@sourceFile$, @image) = #GDIPLUS_OK
totalBufferSize=0
numProperties=0
If GdipGetPropertySize(image, @totalBufferSize, @numProperties) = #GDIPLUS_OK
If totalBufferSize And numProperties
*m=AllocateMemory(totalBufferSize)
rr=gdipGetAllPropertyItems(image,totalBufferSize, numProperties,*m)
; CreateFile(0,"prop.bin")
; WriteData(0,*m,totalBufferSize)
; CloseFile(0)
*pp.PropertyItem=AllocateMemory(SizeOf(PropertyItem))
For i=0 To numProperties-1
CopyMemory(*m+i*SizeOf(PropertyItem),*pp,SizeOf(PropertyItem))
Debug "id="+Hex(*pp\id)+",len="+Str(*pp\length)+",type="+Str(*pp\type);;+",value="+Str(pp\*value)
If *pp\type=1 And *pp\id=$9c9c
; ShowMemoryViewer(*pp\value, *pp\length)
Debug PeekS(*pp\value,*pp\length,#PB_Unicode)
EndIf
;type=1, id=9c9c EXIF comment
If *pp\type=2
Debug PeekS(*pp\value,*pp\length,#PB_Ascii);ShowMemoryViewer(*pp\value, 1024)
EndIf
Next i
FreeMemory(*pp)
FreeMemory(*m)
EndIf
EndIf;If GdipGetPropertySize
GdipDisposeImage(image)
EndIf
; ;Tidy up.
GdiplusShutdown(token)
EndIf
DataSection
;CLSID for the gdi+ jpeg encoder.
clsid_jpeg:
Data.l $557CF401
Data.w $1A04, $11D3
Data.b $9A, $73, $00, $00, $F8, $1E, $F3, $2E
;CLSID for the relevant gdi+ encoder parameter.
clsid_EncoderQuality:
Data.l $1D5BE4B5
Data.w $FA4A, $452D
Data.b $9C, $DD, $5D, $B3, $51, $05, $E7, $EB
EndDataSection
Скормил программе чистую картинку без EXIF и с текстовым маркером. Нифига не получил, так как структура Image Properties описана через задницу:
Structure PropertyItem
id.l; // ID of this property
length.l; // Length of the property value, in bytes
type.u ; // Type of the value, as one of TAG_TYPE_XXX
padding.w ; это поле отсутствовало
*value
EndStructure
id.l; // ID of this property
length.l; // Length of the property value, in bytes
type.u ; // Type of the value, as one of TAG_TYPE_XXX
padding.w ; это поле отсутствовало
*value
EndStructure
Разобрался с выводом данных и получил, что тестовый маркер комментария помечен как PropertyTagExifUserComment $9286. Но мне показалось, что мало, я добавил к файлу ~photo_2025-02-18_14-38-49.jpg свой комментарий, поле помечено как $9C9C.- по спецификации это Exif.Image.XPComment. Подобные исследования может продолжить каждый,
использовав этот код. Но мне показалось мало, и я обратился к формату PNG, скормил файл с порцией iTXt, программа ничего не вывела.
Я стал разбираться с записью комментария к файлу JPG(save_draw_jpg.pb)
;https://www.purebasic.fr/english/viewtopic.php?t=49026
Structure GdiplusStartupInput
GdiPlusVersion.l
*DebugEventCallback.DebugEventProc
SuppressBackgroundThread.l
SuppressExternalCodecs.l
EndStructure
Structure PropertyItem
id.l; // ID of this property
length.l; // Length of the property value, in bytes
type.u ; // Type of the value, as one of TAG_TYPE_XXX
padding.w
*value
EndStructure
;The following two structures are used for setting encoder parameters (in our case the 'quality' parameter).
Structure EncoderParameter
guid.GUID
NumberOfValues.l
Type.l
Value.l
EndStructure
Structure EncoderParameters
Count.i
Parameter.EncoderParameter[1]
EndStructure
Import "gdiplus.lib"
GdiplusStartup(token, *input.GdiplusStartupInput, output)
GdiplusShutdown(token)
GdipLoadImageFromFile(*filename, *bitmap)
GdipImageRotateFlip(image, rotateFlipType)
GdipSaveImageToFile(image, filename.p-unicode, *clsidEncoder.CLSID, *encoderParams)
GdipDisposeImage(image)
GdipGetAllPropertyItems(*image, totalBufferSize.l, numProperties.l, ai);ai is *allItems.PropertyItem
GdipGetPropertySize(*image, totalBufferSize.i, numProperties.i)
GdipCreateBitmapFromHBITMAP(hbm.i, hpal.i, bitmap)
GdipSetPropertyItem(*image, *pi)
EndImport
path$ = GetCurrentDirectory()+"ttest.jpg"
; Make an image to test with
CreateImage(0, 640,640)
StartDrawing(ImageOutput(0))
Box(0,0,640,640,#White)
Circle(319,319,319,#Blue)
Circle(319,319,219,#Green)
Circle(319,319,119,#Yellow)
StopDrawing()
#EncoderParameterValueTypeLong = 4
;Define Unicode$=Space(Len(path$)*2+2), path_as_bstr, lib
;Define bmp.BITMAP,input.GdiplusStartupInput, *token, *image
;PokeS(@Unicode$, path$, -1, #PB_Unicode)
;path_as_bstr = SysAllocString_(@Unicode$)
input.GdiplusStartupInput\GdiPlusVersion=1
GdiplusStartup(@*token, @input, #Null)
; Here is where we have a gdiplus image object that we want to save as jpeg with selectable quality
GdipCreateBitmapFromHBITMAP(ImageID(0), 0, @*image)
quality.l = 100 ; qualities can range from 0 - 100, with 100 being best
With encoderparams.EncoderParameters
\Count = 1
\Parameter[0]\Type = #EncoderParameterValueTypeLong
\Parameter[0]\NumberOfValues = 1
\parameter[0]\Value = @quality
EndWith
CopyMemory(?clsid_EncoderQuality, encoderparams\parameter[0]\Guid, SizeOf(GUID))
c$="simple text"
#PropertyTagImageTitle = $0320
#PropertyTagExifUserComment=$9286
#PropertyTagTypeASCII = 2
*NewTitle.PropertyItem=AllocateMemory(SizeOf(PropertyItem))
NewTitle.s = Space(MemoryStringLength(@c$, #PB_Ascii))
PokeS(@NewTitle, c$, -1, #PB_Ascii)
*NewTitle\id = #PropertyTagExifUserComment;#PropertyTagImageTitle
*NewTitle\length=Len(c$)+1
*NewTitle\type = #PropertyTagTypeASCII
*NewTitle\value = @NewTitle
rr=GdipSetPropertyItem(*image, *NewTitle)
Debug rr
FreeMemory(*NewTitle)
result = GdipSaveImageToFile(*image, path$, ?clsid_jpeg, encoderparams)
GdipDisposeImage(image)
GdiplusShutdown(token)
DataSection
clsid_jpeg: ; clsid for jpeg image format
Data.l $557CF401
Data.w $1A04
Data.w $11D3
Data.b $9A,$73,$00,$00,$F8,$1E,$F3,$2E
clsid_EncoderQuality:
Data.l $1D5BE4B5
Data.w $FA4A
Data.w $452D
Data.b $9C,$DD,$5D,$B3,$51,$05,$E7,$EB
EndDataSection
GDI+ добавляет разные поля в зависимости от
*NewTitle\id = #PropertyTagExifUserComment;#PropertyTagImageTitle
Если использовать #PropertyTagImageTitle, то некоторые программы не понимают этот тег.Или я не осилил описание? Короче, в оригинальном исходном тексте добавляется пользовательский комментарий. Выходит, что текстовый маркер для JPG не пишется. С PNG не стал разбираться, меня утомили эти поиски информации и ковыряние с убогим API.
Исходники с картинками здесь.
Комментарии
Отправить комментарий