Windows Imaging Component

Windows specific forum
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Windows Imaging Component

Post by wilbert »

While looking for ways to encode images, I also encountered WIC.
I was curious about it and ported an example
https://msdn.microsoft.com/en-us/library/windows/desktop/ee719871(v=vs.85).aspx#tiffencoding
Maybe it's interesting for someone.

Include file

Code: Select all

; >>> Constants <<<

Enumeration
  #WICDecodeMetadataCacheOnDemand
  #WICDecodeMetadataCacheOnLoad
EndEnumeration
  
Enumeration
  #WICBitmapEncoderCacheInMemory
  #WICBitmapEncoderCacheTempFile
  #WICBitmapEncoderNoCache
EndEnumeration

Enumeration
  #WICBitmapUseAlpha
  #WICBitmapUsePremultipliedAlpha
  #WICBitmapIgnoreAlpha
EndEnumeration

Enumeration
  #WICBitmapPaletteTypeCustom
  #WICBitmapPaletteTypeMedianCut
  #WICBitmapPaletteTypeFixedBW
  #WICBitmapPaletteTypeFixedHalftone8
  #WICBitmapPaletteTypeFixedHalftone27
  #WICBitmapPaletteTypeFixedHalftone64
  #WICBitmapPaletteTypeFixedHalftone125
  #WICBitmapPaletteTypeFixedHalftone216
  #WICBitmapPaletteTypeFixedWebPalette = 7
  #WICBitmapPaletteTypeFixedHalftone252
  #WICBitmapPaletteTypeFixedHalftone256
  #WICBitmapPaletteTypeFixedGray4
  #WICBitmapPaletteTypeFixedGray16
  #WICBitmapPaletteTypeFixedGray256
EndEnumeration

Enumeration
  #WICBitmapDitherTypeNone
  #WICBitmapDitherTypeSolid = 0
  #WICBitmapDitherTypeOrdered4x4
  #WICBitmapDitherTypeOrdered8x8
  #WICBitmapDitherTypeOrdered16x16
  #WICBitmapDitherTypeSpiral4x4
  #WICBitmapDitherTypeSpiral8x8
  #WICBitmapDitherTypeDualSpiral4x4
  #WICBitmapDitherTypeDualSpiral8x8
  #WICBitmapDitherTypeErrorDiffusion
EndEnumeration

Enumeration
  #WICTiffCompressionDontCare
  #WICTiffCompressionNone
  #WICTiffCompressionCCITT3
  #WICTiffCompressionCCITT4
  #WICTiffCompressionLZW
  #WICTiffCompressionRLE
  #WICTiffCompressionZIP
  #WICTiffCompressionLZWHDifferencing
EndEnumeration


; >>> Structures <<<

Structure PROPBAG2 Align #PB_Structure_AlignC
  dwType.l
  vt.u
  cfType.u
  dwHint.l
  *pstrName
  clsid.CLSID
EndStructure


; >>> Interfaces <<<

Interface IWICImagingFactory Extends IUnknown
  CreateDecoderFromFilename(wzFilename.p-unicode, *pguidVendor, dwDesiredAccess,  metadataOptions, *ppIDecoder)
  CreateDecoderFromStream(*pIStream, *pguidVendor, metadataOptions, *ppIDecoder)
  CreateDecoderFromFileHandle(hFile, *pguidVendor, metadataOptions, *ppIDecoder)
  CreateComponentInfo(clsidComponent, *ppIInfo)
  CreateDecoder(guidContainerFormat, *pguidVendor, *ppIDecoder)
  CreateEncoder(guidContainerFormat, *pguidVendor, *ppIEncoder)
  CreatePalette(*ppIPalette)
  CreateFormatConverter(*ppIFormatConverter)
  CreateBitmapScaler(*ppIBitmapScaler)
  CreateBitmapClipper(*ppIBitmapClipper)
  CreateBitmapFlipRotator(*ppIBitmapFlipRotator)
  CreateStream(*ppIWICStream)
  CreateColorContext(*ppIWICColorContext)
  CreateColorTransformer(*ppIWICColorTransform)
  CreateBitmap(uiWidth, uiHeight, pixelFormat, option, *ppIBitmap)
  CreateBitmapFromSource(*piBitmapSource, option, *ppIBitmap)
  CreateBitmapFromSourceRect(*piBitmapSource, x, y, width, height, *ppIBitmap)
  CreateBitmapFromMemory(uiWidth, uiHeight, pixelFormat, cbStride, cbBufferSize, *pbBuffer, *ppIBitmap)
  CreateBitmapFromHBITMAP(hBitmap, hPalette, options, *ppIBitmap)
  CreateBitmapFromHICON(hIcon, *ppIBitmap)
  CreateComponentEnumerator(componentTypes, options, *ppIEnumUnknown)
  CreateFastMetadataEncoderFromDecoder(*pIDecoder, *ppIFastEncoder)
  CreateFastMetadataEncoderFromFrameDecode(*pIFrameDecoder, *ppIFastEncoder)
  CreateQueryWriter(guidMetadataFormat, *pguidVendor, *ppIQueryWriter)
  CreateQueryWriterFromReader(*pIQueryReader, *pguidVendor, *ppIQueryWriter)
EndInterface

Interface IWICStream Extends IStream
  InitializeFromIStream(Stream.IStream)
  InitializeFromFilename(Filename.p-unicode, AccessMode.l)
  InitializeFromMemory(*Buffer, BufferSize.l)
  InitializeFromIStreamRegion(Stream.IStream, Offset.q, MaxSize.q)
EndInterface

Interface IWICBitmapSource Extends IUnknown
  GetSize(*puiWidth, *puiHeight)
  GetPixelFormat(*pPixelFormat)
  GetResolution(*pDpiX, *pDpiY)
  CopyPalette(*pIPalette)
  CopyPixels(*prc, cbStride, cbBufferSize, *pbBuffer)
EndInterface

Interface IWICBitmap Extends IWICBitmapSource
  Lock(*prcLock, flags, *ppILock)
  SetPalette(*pIPalette)
  SetResolution(dpiX.d, dpiY.d)
EndInterface

Interface IWICFormatConverter Extends IWICBitmapSource
  Initialize(*pISource, dstFormat, dither, *pIPalette, alphaThresholdPercent.d, paletteTranslate)
  CanConvert(srcPixelFormat, dstPixelFormat, *pfCanConvert)
EndInterface

Interface IWICPalette Extends IUnknown
  InitializePredefined(ePaletteType, fAddTransparentColor)
  InitializeCustom(*pColors, colorCount)
  InitializeFromBitmap(*pISurface, colorCount, fAddTransparentColor)
  InitializeFromPalette(*pIPalette)
  GetType(*pePaletteType)
  GetColorCount(*pcCount)
  GetColors(colorCount, *pColors, *pcActualColors)
  IsBlackWhite(*pfIsBlackWhite)
  IsGrayscale(*pfIsGrayscale)
  HasAlpha(*pfHasAlpha)
EndInterface

Interface IWICBitmapDecoder Extends IUnknown
  QueryCapability(*pIStream, *pdwCapability)
  Initialize(*pIStream, cacheOptions)
  GetContainerFormat(*pguidContainerFormat)
  GetDecoderInfo(*ppIDecoderInfo)
  CopyPalette(*pIPalette)
  GetMetadataQueryReader(*ppIMetadataQueryReader)
  GetPreview(*ppIBitmapSource)
  GetColorContexts(cCount, *ppIColorContexts, *pcActualCount)
  GetThumbnail(*ppIThumbnail)
  GetFrameCount(*pCount)
  GetFrame(index, *ppIBitmapFrame)
EndInterface

Interface IWICBitmapEncoder Extends IUnknown
  Initialize(*pIStream, cacheOption)
  GetContainerFormat(*pguidContainerFormat)
  GetEncoderInfo(*ppIEncoderInfo)
  SetColorContexts(cCount,  *ppIColorContext)
  SetPalette(*pIPalette)
  SetThumbnail(*pIThumbnail)
  SetPreview(*pIPreview)
  CreateNewFrame(*ppIFrameEncode, *ppIEncoderOptions)
  Commit()
  GetMetadataQueryWriter(*ppIMetadataQueryWriter)
EndInterface

Interface IWICBitmapFrameDecode Extends IWICBitmapSource
  GetMetadataQueryReader(*ppIMetadataQueryReader)
  GetColorContexts(cCount, *ppIColorContexts, *pcActualCount)
  GetThumbnail(*ppIThumbnail)
EndInterface

Interface IWICBitmapFrameEncode Extends IUnknown
  Initialize(*pIEncoderOptions)
  SetSize(uiWidth, uiHeight)
  SetResolution(dpiX.d, dpiY.d)
  SetPixelFormat(*pPixelFormat)
  SetColorContexts(cCount, *ppIColorContext)
  SetPalette(*pIPalette)
  SetThumbnail(*pIThumbnail)
  WritePixels(lineCount, cbStride, cbBufferSize, *pbPixels)
  WriteSource(*pIBitmapSource, *prc)
  Commit()
  GetMetadataQueryWriter(*ppIMetadataQueryWriter)
EndInterface


; >>> Imports <<<

CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
  Import ""
    MakeBSTR(str.p-unicode) As "_SysAllocString"
  EndImport
CompilerElse
  Import ""
    MakeBSTR(str.p-unicode) As "SysAllocString"
  EndImport
CompilerEndIf


; >>> Macros <<<

Macro SafeRelease(obj)
  If obj : obj\Release() : EndIf
EndMacro

Macro Define_ID(label,l1,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8)
  label:
  !dd l1
  !dw w1,w2
  !db b1,b2,b3,b4,b5,b6,b7,b8
EndMacro


; >>> Data Section <<<

DataSection
  
  Define_ID(CLSID_WICImagingFactory        , 0xcacaf262, 0x9370, 0x4615, 0xa1, 0x3b, 0x9f, 0x55, 0x39, 0xda, 0x4c, 0x0a)
  Define_ID(IID_IWICImagingFactory         , 0xec5ec8a9, 0xc395, 0x4314, 0x9c, 0x77, 0x54, 0xd7, 0xa9, 0x35, 0xff, 0x70)
  ; Container formats
  Define_ID(GUID_ContainerFormatAdng       , 0xf3ff6d0d, 0x38c0, 0x41c4, 0xb1, 0xfe, 0x1f, 0x38, 0x24, 0xf1, 0x7b, 0x84)
  Define_ID(GUID_ContainerFormatBmp        , 0x0af1d87e, 0xfcfe, 0x4188, 0xbd, 0xeb, 0xa7, 0x90, 0x64, 0x71, 0xcb, 0xe3)
  Define_ID(GUID_ContainerFormatPng        , 0x1b7cfaf4, 0x713f, 0x473c, 0xbb, 0xcd, 0x61, 0x37, 0x42, 0x5f, 0xae, 0xaf)
  Define_ID(GUID_ContainerFormatIco        , 0xa3a860c4, 0x338f, 0x4c17, 0x91, 0x9a, 0xfb, 0xa4, 0xb5, 0x62, 0x8f, 0x21)
  Define_ID(GUID_ContainerFormatJpeg       , 0x19e4a5aa, 0x5662, 0x4fc5, 0xa0, 0xc0, 0x17, 0x58, 0x02, 0x8e, 0x10, 0x57)
  Define_ID(GUID_ContainerFormatTiff       , 0x163bcc30, 0xe2e9, 0x4f0b, 0x96, 0x1d, 0xa3, 0xe9, 0xfd, 0xb7, 0x88, 0xa3)
  Define_ID(GUID_ContainerFormatGif        , 0x1f8a5601, 0x7d4d, 0x4cbd, 0x9c, 0x82, 0x1b, 0xc8, 0xd4, 0xee, 0xb9, 0xa5)
  Define_ID(GUID_ContainerFormatWmp        , 0x57a37caa, 0x367a, 0x4540, 0x91, 0x6b, 0xf1, 0x83, 0xc5, 0x09, 0x3a, 0x4b)
  ; Pixel formats
  Define_ID(GUID_WICPixelFormat8bppIndexed , 0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x04)
  Define_ID(GUID_WICPixelFormat8bppGray    , 0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x08)
  Define_ID(GUID_WICPixelFormat24bppBGR    , 0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0c)
  Define_ID(GUID_WICPixelFormat32bppBGRA   , 0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0f)
  
EndDataSection
Ported example

Code: Select all

; >>> Ported example code <<<

Factory.IWICImagingFactory
Encoder.IWICBitmapEncoder
BitmapFrame.IWICBitmapFrameEncode
PropertyBag.IPropertyBag2

Stream.IWICStream
Width = 640
Height = 480

If CoInitializeEx_(0,2)&-2 = 0
  uninit = #True
EndIf

hr = CoCreateInstance_(?CLSID_WICImagingFactory, #Null, #CLSCTX_INPROC_SERVER, ?IID_IWICImagingFactory, @Factory)

If hr = 0
  hr = Factory\CreateStream(@Stream.IWICStream)
EndIf

If hr = 0
  hr = Stream\InitializeFromFilename("output.tif", #GENERIC_WRITE)
EndIf

If hr = 0
  hr = Factory\CreateEncoder(?GUID_ContainerFormatTiff, #Null, @Encoder.IWICBitmapEncoder)
EndIf

If hr = 0
  hr = Encoder\Initialize(Stream, #WICBitmapEncoderNoCache)
EndIf

If hr = 0
  Encoder\CreateNewFrame(@BitmapFrame.IWICBitmapFrameEncode, @PropertyBag.IPropertyBag2)
EndIf

If hr = 0
  ; This is how you customize the TIFF output.
  Option.PROPBAG2
  Option\pstrName = MakeBSTR("TiffCompressionMethod")
  VarValue.VARIANT
  VariantInit_(@VarValue)
  VarValue\vt = #VT_UI1
  VarValue\bVal = #WICTiffCompressionZIP
  hr = PropertyBag\Write(1, @Option, @VarValue)
  SysFreeString_(Option\pstrName)
  If hr = 0
    hr = BitmapFrame\Initialize(PropertyBag)
  EndIf
EndIf

If hr = 0
  hr = BitmapFrame\SetSize(Width, Height)
EndIf

CopyStructure(?GUID_WICPixelFormat24bppBGR, @FormatGUID.GUID, GUID)
If hr = 0
  hr = BitmapFrame\SetPixelFormat(@FormatGUID)
EndIf

If hr = 0
  ; We're expecting to write out 24bppRGB. Fail if the encoder cannot do it.
  hr = Bool(CompareMemory(@FormatGUID, ?GUID_WICPixelFormat24bppBGR, 16) = 0)
EndIf

If hr = 0
  Stride = (Width * 24 + 7)/8;***WICGetStride***
  BufferSize = Height * Stride
  *Buffer = AllocateMemory(BufferSize)
  *BufferPtr.Byte = *Buffer

  If *Buffer <> #Null
    For i = 0 To BufferSize - 1
      *BufferPtr\b = Random(255)
      *BufferPtr + 1
    Next
    
    hr = BitmapFrame\WritePixels(Height, Stride, BufferSize, *Buffer)

    FreeMemory(*Buffer)
  Else
    hr = #E_OUTOFMEMORY
  EndIf
EndIf

If hr = 0
  hr = BitmapFrame\Commit()
EndIf

If hr = 0
  hr = Encoder\Commit()
EndIf

If Factory
  Factory\Release()
EndIf

If BitmapFrame
  BitmapFrame\Release()
EndIf

If Encoder
  Encoder\Release()
EndIf

If Stream
  Stream\Release()
EndIf

If hr = 0
  MessageRequester("TIFF encoding example", "TIFF file succesfully written")
Else  
  MessageRequester("TIFF encoding example", "An error has occured")
EndIf

If uninit
  CoUninitialize_()
EndIf
Last edited by wilbert on Tue Sep 13, 2016 2:29 pm, edited 4 times in total.
Windows (x64)
Raspberry Pi OS (Arm64)
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Windows Imaging Component

Post by wilbert »

GIF encoding example

Code: Select all

; >>> GIF encoding example <<<

; Load an image.
UsePNGImageDecoder()
LoadImage(0, "myImage.png")
Width = ImageWidth(0)
Height = ImageHeight(0)

If CoInitializeEx_(0,2)&-2 = 0
  uninit = #True
EndIf

hr = CoCreateInstance_(?CLSID_WICImagingFactory, #Null, #CLSCTX_INPROC_SERVER, ?IID_IWICImagingFactory, @Factory.IWICImagingFactory)

If hr = #S_OK
  ; Create Stream, Encoder, Palette, Bitmap and FormatConverter objects.
  hr | Factory\CreateStream(@Stream.IWICStream)
  hr | Factory\CreateEncoder(?GUID_ContainerFormatGif, #Null, @Encoder.IWICBitmapEncoder)
  hr | Factory\CreatePalette(@Palette.IWICPalette)
  hr | Factory\CreateBitmapFromHBITMAP(ImageID(0), #Null, #WICBitmapUseAlpha, @Bitmap.IWICBitmap)
  hr | Factory\CreateFormatConverter(@FormatConverter.IWICFormatConverter)
EndIf

If hr = #S_OK
  ; Initialize stream and encoder.
  hr | Stream\InitializeFromFilename("output.gif", #GENERIC_WRITE)
  hr | Encoder\Initialize(Stream, #WICBitmapEncoderNoCache)
  ; Create new frame.
  hr | Encoder\CreateNewFrame(@BitmapFrame.IWICBitmapFrameEncode, @PropertyBag.IPropertyBag2)
  ; Convert the bitmap to 8bppIndexed.
  hr | Palette\InitializeFromBitmap(Bitmap, 256, #True)
  hr | FormatConverter\Initialize(Bitmap, ?GUID_WICPixelFormat8bppIndexed, #WICBitmapDitherTypeErrorDiffusion,
                                  Palette, 50, #WICBitmapPaletteTypeCustom)
EndIf

; Release the Bitmap object as it isn't needed anymore.
SafeRelease(Bitmap)

If hr = #S_OK
  ; Initialize bitmap frame and set size.
  hr | BitmapFrame\Initialize(PropertyBag)
  hr | BitmapFrame\SetSize(Width, Height)
  ; Try to set pixel format to 8bppIndexed.
  CopyMemory(?GUID_WICPixelFormat8bppIndexed, @FormatGUID.GUID, 16)
  hr | BitmapFrame\SetPixelFormat(@FormatGUID)
  ; Check if the format could be set.
  hr | Bool(CompareMemory(@FormatGUID, ?GUID_WICPixelFormat8bppIndexed, 16) = 0)
EndIf

If hr = #S_OK
  ; Write and commit.
  hr | BitmapFrame\WriteSource(FormatConverter, #Null)
  hr | BitmapFrame\Commit()
  hr | Encoder\Commit()
EndIf

; Release the objects
SafeRelease(BitmapFrame)
SafeRelease(Palette)
SafeRelease(FormatConverter)
SafeRelease(Encoder)
SafeRelease(Stream)
SafeRelease(Factory)

If hr = 0
  MessageRequester("GIF encoding example", "GIF file succesfully written")
Else  
  MessageRequester("GIF encoding example", "An error has occured")
EndIf

If uninit
  CoUninitialize_()
EndIf
Last edited by wilbert on Tue Sep 13, 2016 5:57 am, edited 2 times in total.
Windows (x64)
Raspberry Pi OS (Arm64)
es_91
Enthusiast
Enthusiast
Posts: 298
Joined: Thu Jan 27, 2011 12:00 pm
Location: DE

Re: Windows Imaging Component

Post by es_91 »

Dear wilbert,

without digging your code now, i think it is great work and important. But how is it with the .gif format, are not still there patents or something fuzzy on it?

Program brings errors but i'll take a deeper look, later. Thank you very much.

:D
:mrgreen:
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Windows Imaging Component

Post by wilbert »

es_91 wrote:But how is it with the .gif format, are not still there patents or something fuzzy on it?
To be honest, I don't know how it is with the patents.
I assumed using the encoder that is built into Windows would be fine :?
es_91 wrote:Program brings errors but i'll take a deeper look, later. Thank you very much.
I tested it with PB 5.42 on Windows 10.
Windows (x64)
Raspberry Pi OS (Arm64)
es_91
Enthusiast
Enthusiast
Posts: 298
Joined: Thu Jan 27, 2011 12:00 pm
Location: DE

Re: Windows Imaging Component

Post by es_91 »

Should all be fine. I did not look close enough. Again, thanks. 8) I always loved .gif for when you reconvert it to .bmp (8 bit using palette, which it then - after being .gif - does by default*) and compress it with LZMA/zip it's smaller than .png and better than .jpg to my experience and opinion.

Tough little file format.

(* in smart conversion tools such as paint.net)
:mrgreen:
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Re: Windows Imaging Component

Post by netmaestro »

The last GIF patent to expire was IBM's in 2006. There have not been any patent holders for the GIF format for ten years. With virtually everyone using the format these days, the web is replete with uncounted millions of the things and if anyone were to claim copyright ownership on them, everyone would know all about it within a matter of hours. But patent database searches done by GNU and SFLC have turned up nothing past 2006, and with ten quiet years of steady proliferation behind us, I think we can all relax and stop looking over our shoulders for the GIF bogeyman. Good heavens, if that creature were to ever rear its ugly head it would give poor KCC a heart attack.
BERESHEIT
User avatar
VB6_to_PBx
Enthusiast
Enthusiast
Posts: 627
Joined: Mon May 09, 2011 9:36 am

Re: Windows Imaging Component

Post by VB6_to_PBx »

Good heavens, if that creature were to ever rear its ugly head it would give poor KCC a heart attack.
+ 1000

:D
 
PureBasic .... making tiny electrons do what you want !

"With every mistake we must surely be learning" - George Harrison
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Windows Imaging Component

Post by Kwai chang caine »

MasterFrog wrote:Good heavens, if that creature were to ever rear its ugly head it would give poor KCC a heart attack.
:lol: :lol:

Don't worry for me, kind master of the net.... 8)
For the bad creature i have him :mrgreen:

Image
ImageThe happiness is a road...
Not a destination
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Windows Imaging Component

Post by Kwai chang caine »

Thanks WILBERT i have tested your code, and it works very well 8)
ImageThe happiness is a road...
Not a destination
es_91
Enthusiast
Enthusiast
Posts: 298
Joined: Thu Jan 27, 2011 12:00 pm
Location: DE

Re: Windows Imaging Component

Post by es_91 »

Thanks netmaestro for the info on .gif. Much appreciated!

Now since 8 bit looks dirty on larger images, is there a way to use .gif with a slightly higher (10, )12(, 14) or 16 bits resolution?

And, in case, if not is .png the only way to store 16 bit images in a such-similar matter? (no .jpeg here, please.)

( edited )
:mrgreen:
Post Reply