Some specifications of WebPEncodeLosslessBGRA function from libwebp Api
WebPEncodeLosslessBGRA
This function encodes images pointed to by bgra parameter and returns the WebP image file and it´s size.
It compresses the image using the lossless format. Files are usually larger than lossy format, but will not suffer any compression loss. For lossy compression, you can use the WebPEncodeRGBA family.
The lossy functions (WebPEncodeLosslessRGB, WebPEncodeLosslessBGR, WebPEncodeLosslessRGBA and WebPEncodeLosslessBGRA), use the library's default settings. For lossless this means exact is disabled. RGB values in transparent areas will be modified to improve compression.
To avoid this, use WebPEncode() and set WebPConfig::exact to 1.
Parameters:
bgra (in) - Pointer to the Pixel data of the image in BGRA format. So, it points to a Array of RGBQUAD structures. You can retrieve the Pixels from gdiplus, simple gdi or whatever other library
Width (in) - An integer representing the width of the image (In pixels)
Height (in) - An integer representing the height of the image (In pixels)
stride (in) - An integer specifying the stride of the image (In bytes). It specifies the byte offset between the beginning of one scan line and the next.
This is usually (but not necessarily) the number of bytes in the pixel format (for example, 2 for 16 bits per pixel and 4 for 24 or 32bpp) multiplied by the width of the bitmap.
An easier way to calculate the stride is with GetStride function showed below
output(out) - Pointer to a variable (int) where the WebpImage will be stored
Return Values:
If the function suceeds, it returns the size of the converted image
If the function fails, it returns 0
Remarks:
The function allocates internally enough memory to do the convertion, so, after using the function, you must deallocate the memory with WebPFree Api.
Example of usage:
call GetStride D@ImgWidth, 32
lea ecx D@OutData | mov D$ecx 0
call 'libwebp.WebPEncodeLosslessBGRA' D@PixData, D@ImgWidth, D@ImgHeight, eax, ecx
call 'libwebp.WebPFree' D@OutData ; release the allocated memory used internally in libwebp library
More info (although very very incomplete) can be found at:
https://developers.google.com/speed/webp/docs/api
Additional Function:
GetStride
;;
GetStride
This function calculates the Line Stride of the Image given a BitCount, biPlane and Width input.
Similar to GetLineStrideFromBmpNfoHdr Api, Line Strides (also known as RowSize, line padding or Stride) are basically
the number of bytes occupied in memory by a line of an image.
It specifies the byte offset between the beginning of one scan line and the next.
This is usually (but not necessarily) the number of bytes in the pixel format (for example, 2 for 16 bits per pixel) multiplied by the width of the bitmap.
In GdiPlus Api, the value passed as a Stride (in GdipCreateBitmapFromScan0 for example), must be a multiple of four.
Parameters:
Width - A integer representing the width of the image
BitCount - A integer representing the bit count of the image. It specifies the number of bits per pixel (bpp).
For uncompressed formats, this value is the average number of bits per pixel.
For compressed formats, this value is the implied bit depth of the uncompressed image,
after the image has been decoded.
Allowed values are: 1, 4, 8, 16, 24 and 32
Return value:
The function will return in eax the stride of a given bitmap image. (In bytes)
Remarks:
Stride is calculated as a padding bytes. The following table illustrates the relationship between width and stride.
width stride
1 4
2 4
3 4
4 4
5 8
6 8
7 8
8 8
9 12
10 12
11 12
12 12
......
Be carefull when using the returned value, because it is always in Bytes, but some Apis may uses the stride as a multiple of 4 (to represent the RGBQUAD values)
On google Libwebp library, gdiplus, normal gdi, FGetDIBits or GdipSaveWebpImage, the returned value are in Bytes, so we don´t need to divide it by 4.
The function works for:
BITMAPINFOHEADER, BITMAPV2INFOHEADER, BITMAPV3INFOHEADER, BITMAPV4INFOHEADER, BITMAPV5INFOHEADER
Example of usage:
a) With libwebp:
call GetStride D@ImgWidth, 32
lea ecx D@OutData | mov D$ecx 0
call 'libwebp.WebPEncodeLosslessBGRA' D@PixData, D@ImgWidth, D@ImgHeight, eax, ecx
b) With gdiplus:
call GetStride D@ImgWidth, 32
call 'gdiplus.GdipCreateBitmapFromScan0' D@ImageWidth, D@ImageHeight, eax, PIXELFORMAT_32BPPARGB, D@ImgBits, D@pImage
c) with gdi to calculate the amount of memory neded to build the pixels data
call GetLineStrideFromBmpNfoHdr OutBmpNfo
Align_On 4 eax | imul eax D@BITMAP.bmHeightDis | mov D@RawImgDataSize eax
mov D@TmpMem 0 | lea eax D@TmpMem
call 'RosMem.VMemAlloc' eax, D@RawImgDataSize
References: http://mapw.elte.hu/elek/bmpinmemory.html
https://medium.com/@oleg.shipitko/what-does-stride-mean-in-image-processing-bba158a72bcd
https://www.collabora.com/news-and-blog/blog/2016/02/16/a-programmers-view-on-digital-images-the-essentials/
;;
Proc GetStride::
Arguments @Width, @BitCount
Local @cClrBits
Uses edx
; Convert the color format to a count of bits. No need to calculate the bitplanes sicne it is always 1 for bitmaps
movzx eax W@BitCount
If ax <= 1
mov eax 1
Else_If ax <= 4
mov eax 4
Else_If ax <= 8
mov eax 8
Else_If ax <= 16
mov eax 16
Else_If ax <= 24
mov eax 24
Else
mov eax 32
End_If
mov D@cClrBits eax
; Compute the number of bytes in the array of color indices and store the result in biSizeImage.
; For Windows NT, the width must be DWORD aligned unless the bitmap is RLE compressed. This example shows this.
; For Windows 95/98/Me, the width must be WORD aligned unless the bitmap is RLE compressed.
movzx eax W@Width
imul eax D@cClrBits | add eax 31 | and eax (0-32)
cdq | and edx 7 | add eax edx | sar eax 3
EndP
Libwebp (v 1.1.0) Library attached
Interesting, Gustavo. Does it compress better than PNG?
Hi JJ. Much much better.
the documentation really sucks. I spent the last 2 days trying to find a way to encode and decode this stuff, but it is worthfull and it is relatively simple to do :)
I created a function to convert a GdiPlus image to this webp format. It is like this:
;;
GdipSaveWebpImage
This function saves a given GdiPlus image to a Webp file (Google image file for the web).
Parameters:
pGdiPlusImage(in) - Pointer to the GDIPlus Image object.
pFilename(in) - Pointer to a Null terminated Ansi string that specifies the path and the filename for the saved image.
Return Value:
If the function suceeeds, it returns &TRUE
If fails returns &FALSE
Remarks:
The function uses internally libwebp library v 1.1.0 (google image library for the web), so you must have in your system or in
the same directory as your app where FastCrt library is, the file libwebp.dll
Example of usage:
[MyFileName: B$ "c:\MyFile.webp", 0]
call 'FastCRT.GdipSaveWebpImage' D@pImage, MyFileName
References:
http://www.jose.it-berater.org/smfforum/index.php?topic=1858.msg6500#msg6500
https://docs.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-listing-parameters-and-values-for-all-encoders-use
https://docs.rs/libwebp/0.1.2/libwebp/fn.WebPEncodeLosslessRGBA.html
https://ffmpeg.org/doxygen/3.1/libwebpenc_8c_source.html
https://docs.rs/libwebp-sys2/0.1.1/libwebp_sys/fn.WebPEncode.html
https://docs.rs/libwebp-sys2/0.1.1/libwebp_sys/struct.WebPPicture.html
https://hg.libsdl.org/SDL_image
https://hg.libsdl.org/SDL/
http://game.gamezone.space/ufo-run/cocos2d-x-2.2/cocos2dx/platform/third_party/win32/libwebp/
You can download newer versions of libwebp library at:
https://developers.google.com/speed/webp/docs/precompiled
https://storage.googleapis.com/downloads.webmproject.org/releases/webp/index.html
;;
Proc GdipSaveWebpImage::
Arguments @pImage, @pFileName
Local @ImgHeight, @ImgWidth, @hBitMap, @PixData, @Stride, @OutData, @ReturnValue
Uses ecx, edx
mov D@ReturnValue &FALSE
lea eax D@ImgWidth
call 'gdiplus.GdipGetImageWidth' D@pImage, eax
lea eax D@ImgHeight
call 'gdiplus.GdipGetImageHeight' D@pImage, eax
lea eax D@hBitMap
call 'gdiplus.GdipCreateHBITMAPFromBitmap' D@pImage, eax, 0 ;0FF000000; The last parameter is in RGBQUAD: format Ex: 0FF000000 or simply 0 if the background is tranbsparent
lea eax D@PixData
call FGetDIBits D@hBitMap, eax, &TRUE ; pixels is obtained in format RGBQUAD; (Blue, Red, Green and Alpha) and not RGBA ;. This is just a function i made to get the pixels of a image. (A bit lomng to post here, but basically you need to get the true pixel data and put here. (In RGBQUAD format)
; FGetDibits creates a Pixel data in RGQUAD format creating a Bitmap in 32 bpp with 1 plane, therefore we need to calculate
; the stride from it. There´s a internal function inside RosAsm FAstCRT library called FGetDIBitsEx that do the similar thing (GetLineStrideFromBmpNfoHdr) (I´ll release this library soon. I need to fix some damn bugs in RosAsm 1st)
call GetStride D@ImgWidth, 32 ; <---- As explained in the 1st post
lea ecx D@OutData | mov D$ecx 0
;call 'libwebp.WebPEncodeLosslessRGBA' D@PixData, D@ImgWidth, D@ImgHeight, eax, ecx ; <--- This Api will exchange Red with Blue. Incorrect, but fun to see :)
call 'libwebp.WebPEncodeLosslessBGRA' D@PixData, D@ImgWidth, D@ImgHeight, eax, ecx
; returned in eax the size of the image
.If eax <> 0
call FileCreate D@pFileName, D@OutData, eax
call 'libwebp.WebPFree' D@OutData
mov D@ReturnValue &TRUE
.End_If
call 'RosMem.VMemFree' D@PixData
mov eax D@ReturnValue
EndP
___________________________________________________________________________________________
I´m not sure if i configured the Api correctly, but it is finally saving it as webp. In fact, i didn´t configured it at all. I simply used the Api directly to see how it worked. The difference of quality don´t seems to me be that big (afterall i didn´t configured it) :biggrin: :biggrin:.
The size is reduced way better then in png, for example. I´m posting it here the tests i did :)
Btw, GdiPlus and a Library called Wic also handles this webp format, but i don´t know if they are something only available for Windows10 and neither if they save the files.
On GdiPlus for example it do contains the GUID to webp format, but i don´t know how to activated it to see if it can decode or encode or both
On Wic, it do decodes the webpformat, but i don´t know if it encodes.
Other format of interest that i plan to make the proper functions to encode and decode is the heic file. It can be loaded directly without having to install any windows10 plugin or codec (And most likely will work on older windows versions as well).
https://www.libde265.org/downloads-software/
https://mpeg.chiariglione.org/standards/mpeg-h/image-file-format
https://github.com/strukturag/libheif
https://github.com/nokiatech/heif
I´ll check this format later. Gdiplus also has the GUID for it, but apparently as with webp it does not have the proper encoders or decoders for all windows versions, that´s why i choose to use the dlls directly from them.
The only problem i see on those libraries is the fact that they must be located on the same directory as the main file (In case: RosAsm.exe), i would like to know if there is way to put them on their own directory like a plugin for example (preference without using loadlibrary api.) Ex: i want to create a directory named as "data" or 'libraries" or "plugin" etc, put those dlls there and keep the main directory only with the main executable file (RosAsm.exe)
Hi Guga,
GdiPlus can compress your 307254 bytes .BMP, to a 3885 bytes .PNG file.
What's your secret trick, Marinus? I get 8922 bytes for PNG, 8531 for GIF :cool:
include \masm32\MasmBasic\Res\MbGui.asm
Event Paint
SaveImageToFile "GugaBitmap.png" ; 8922 bytes
GuiImage "GugaBitmap.bmp"
EndOfCode
Hi Marinus.
I saw that. You need to use the EncoderParameter Structure, right ? I used that to create Tiff files, as well.
JJ, you may need to setup the Encoder Structure, like this example in tiff.
[ENCODER_PARAMETER_VALUE_TYPE_BYTE 1 ; 8-bit unsigned int
ENCODER_PARAMETER_VALUE_TYPE_ASCII 2 ; 8-bit byte containing one 7-bit ASCII code. NULL terminated.
ENCODER_PARAMETER_VALUE_TYPE_SHORT 3 ; 16-bit unsigned int
ENCODER_PARAMETER_VALUE_TYPE_LONG 4 ; 32-bit unsigned int
ENCODER_PARAMETER_VALUE_TYPE_RATIONAL 5 ; Two Longs. The first Long is the numerator, the second Long expresses the denomintor.
ENCODER_PARAMETER_VALUE_TYPE_LONGRANGE 6 ; Two longs which specify a range of integer values. The first Long specifies the lower end and the second one specifies the higher end. All values are inclusive at both ends
ENCODER_PARAMETER_VALUE_TYPE_UNDEFINED 7 ; 8-bit byte that can take any value depending on field definition
ENCODER_PARAMETER_VALUE_TYPE_RATIONALRANGE 8] ; Two Rationals. The first Rational specifies the lower end and the second specifies the higher end. All values are inclusive at both ends
[CLSID_TIF: D$ 0557CF405, W$ 01A04, 011D3, B$ 09A, 073, 0, 0, 0F8, 01E, 0F3, 02E]
[TiffEncParam:
TiffEncParam.Count: D$ 2
; Fill and EncoderParameter structure with the compression
TiffEncParam.Parameter1.Guid.Data1: D$ 0E09D739D ; GUID_ENCODER_COMPRESSION
TiffEncParam.Parameter1.Guid.Data2: W$ 0CCD4
TiffEncParam.Parameter1.Guid.Data3: W$ 044EE
TiffEncParam.Parameter1.Guid.Data4: B$ 08E, 0BA, 03F, 0BF, 08B, 0E4, 0FC, 058
TiffEncParam.Parameter1.NumberOfValues: D$ 1
TiffEncParam.Parameter1.Type: D$ ENCODER_PARAMETER_VALUE_TYPE_LONG
TiffEncParam.Parameter1.Value: D$ TiffCompression
; Fill and EncoderParameter structure with the color depth
TiffEncParam.Parameter2.Guid.Data1: D$ 066087055 ; GUID_ENCODER_COLORDEPTH
TiffEncParam.Parameter2.Guid.Data2: W$ 0AD66
TiffEncParam.Parameter2.Guid.Data3: W$ 04C7C
TiffEncParam.Parameter2.Guid.Data4: B$ 09A, 018, 038, 0A2, 031, 0B, 083, 037
TiffEncParam.Parameter2.NumberOfValues: D$ 1
TiffEncParam.Parameter2.Type: D$ ENCODER_PARAMETER_VALUE_TYPE_LONG
TiffEncParam.Parameter2.Value: D$ TiffEncoderColorDepth]
[TiffCompression: D$ 0]
[TiffEncoderColorDepth: D$ 0]
call 'gdiplus.GdipSaveImageToFile' D@pGdiPlusImage, D@FilenameWDis, CLSID_TIF, TiffEncParam
But, using the CLSID for png, right marinus ?
http://www.jose.it-berater.org/smfforum/index.php?topic=1858.msg6500#msg6500
Another thing...How can i create a bitmap in memory (containing the headers) from a gdi pixel data ? Does GdipCreateBitmapFromFile generates a full Bitmap ?
I suceeded to open jpg, png, tiff files using your routine, but how to convert it to a full bitmap so i can manipulate it´s contents (including the headers) directly ?
Other question. How i convert the pixel format without using GdipBitmapConvertFormat ? I mean, say i open a image (jpg, for example) and figure it out that it is 24bpp. How can i convert it directly to 32bpp ?
I made a small routine to scan the pixel data taking onto account the bpp, but what happens if the opened file is 24bpp ? When i pass it to GdipCreateBitmapFromScan0 will it automatically convert to 32bpp or i need to setup it to 32 ?
I mean, i did this:
lea eax D@PixFmt
call 'gdiplus.GdipGetImagePixelFormat' D@ImagePix, eax
If eax <> &S_OK
call 'gdiplus.GdipDisposeImage' D@pImage ; Dispose image in case of failure
xor eax eax
ExitP
End_If
call GdipGetPixelFormatSize D@PixFmt ; <---- Identity if it is 24bp, 1bpp, 4bpp, 32bpp etc
call GetStride D@ImgWidth, eax
call 'gdiplus.GdipCreateBitmapFromScan0' D@ImgWidth, D@ImgHeight, eax, D@PixFmt, &NULL, D@pImage
If eax <> &S_OK
call 'gdiplus.GdipDisposeImage' D@pImage ; Dispose image in case of failure
xor eax eax
Else
mov eax &TRUE
End_If
Say that at GdipGetPixelFormatSize it displays it as a 24bpp. To i create a 32bp should i do something like this ?
lea eax D@PixFmt
call 'gdiplus.GdipGetImagePixelFormat' D@ImagePix, eax
If eax <> &S_OK
call 'gdiplus.GdipDisposeImage' D@pImage ; Dispose image in case of failure
xor eax eax
ExitP
End_If
;mov eax D@ImgWidth | shl eax 2
call GdipGetPixelFormatSize D@PixFmt
If eax <> 32
mov D@PixFmt PIXELFORMAT_32BPPARGB
End_If
call GetStride D@ImgWidth, eax
call 'gdiplus.GdipCreateBitmapFromScan0' D@ImgWidth, D@ImgHeight, eax, D@PixFmt, &NULL, D@pImage
If eax <> &S_OK
call 'gdiplus.GdipDisposeImage' D@pImage ; Dispose image in case of failure
xor eax eax
Else
mov eax &TRUE
End_If
Or...also even a more direct way right below GdipGetImagePixelFormat, like:
lea eax D@PixFmt
call 'gdiplus.GdipGetImagePixelFormat' D@ImagePix, eax
If eax <> &S_OK
call 'gdiplus.GdipDisposeImage' D@pImage ; Dispose image in case of failure
xor eax eax
ExitP
End_If
If D@PixFmt <> PIXELFORMAT_32BPPARGB ; IF pixel format is not 32bpp ARGB, set to 32bpp
mov D@PixFmt PIXELFORMAT_32BPPARGB
End_If
Hi Jochen,
I looked for the maximum count of unique colors in the image guga used.
In guga's .bmp image there are 10 unique colors.
I saved the image as an indexed color image with only 10 entries in the Color Palette.
Now the image only needs 4 bits per index to 1 of the 10 colors.
It creates a lossless image as small as possible.
You can use this routine for it:
; save Color Indexed images with 2 to 256 colors. ( any number between 2 to 256 )
SaveColorIndexedImage proc pGdiPlusImage:DWORD,pFilename:DWORD,Image_Type:DWORD,Color_Count:DWORD
mov ecx,Color_Count
mov eax,offset ColorPalette
mov dword ptr [eax],PaletteFlagsGrayScale
mov dword ptr [eax+4],ecx
invoke GdipInitializePalette,eax,PaletteTypeOptimal,dword ptr [eax+4],FALSE,pGdiPlusImage
test eax,eax
jnz Done
mov ecx,Color_Count
mov eax,PixelFormat8bppIndexed
cmp ecx,16
ja ConvertFormat
mov eax,PixelFormat4bppIndexed
cmp ecx,2
ja ConvertFormat
mov eax,PixelFormat1bppIndexed
ConvertFormat:
invoke GdipBitmapConvertFormat,pGdiPlusImage,eax,DitherTypeSolid,PaletteTypeOptimal,offset ColorPalette,0
test eax,eax
jnz Done
invoke MultiByteToWideChar,CP_ACP,0,pFilename,-1,offset FilenameW,MAX_PATH-1
mov eax,Image_Type
mov byte ptr[CLSID_ImageTypes],al
invoke GdipSaveImageToFile,pGdiPlusImage,offset FilenameW,offset CLSID_ImageTypes,NULL
Done:
ret
SaveColorIndexedImage endp
Function call:
invoke GdipCreateBitmapFromFile,offset FilenameW,offset pImage
invoke SaveColorIndexedImage,pImage,TEXT_("Guga_Bitmap.png"),Image_PNG,10
The routines are also posted in the thread: Bare Minimum GDIplus code to save different Image Types.
http://masm32.com/board/index.php?topic=8483.0
Sources GDIplusColors.zip
http://masm32.com/board/index.php?action=dlattach;topic=8483.0;attach=10350
NOTE: You can remove the "invoke GdipCloneImage,pGdiPlusImage,offset pImageTemp" and "invoke GdipDisposeImage,pImageTemp" lines.
I used "GdipCloneImage" function to use the same bitmap image over and over again to save multiple image types as an example.
Hi Guga,
I'm writing the example sources as we speak, hope to finish it tonight, it will give an answer to all your questions. :biggrin:
Quote from: Siekmanski on October 02, 2020, 03:44:11 AM
Hi Guga,
I'm writing the example sources as we speak, hope to finish it tonight, it will give an answer to all your questions. :biggrin:
Wahooo :biggrin: :biggrin: :biggrin: :biggrin: :thumbsup: :thumbsup: :thumbsup: :thumbsup: :thumbsup:
Quote from: Siekmanski on October 02, 2020, 03:31:23 AMI looked for the maximum count of unique colors in the image guga used.
In guga's .bmp image there are 10 unique colors.
I saved the image as an indexed color image with only 10 entries in the Color Palette.
Now the image only needs 4 bits per index to 1 of the 10 colors.
It creates a lossless image as small as possible.
Hi Marinus,
I had noticed that Guga's bmp has only a few colours, and my old PaintShop Pro knows how to save 16-colour PNGs. So I had a suspicion that you were going for a palette ;-)
Thanks for all the code, this is great stuff :thumbsup:
Thanks. :thumbsup:
Hi Guga,
Posted the sources here: http://masm32.com/board/index.php?topic=8483.msg96272#msg96272
Thank you a lot, marinus. I´ll take a look :)