Hi Guys
I´m trying to create a variation of StretchBlt function but i´m having problems when adjusting the screen coordinates:(
The problem is with the nXOriginDest, nYOriginDest etc. My function uses the parameters of the strecthblt to pass them onto the bitblt api.
Proc StretchBltEx:
Arguments @pCImgStruct, @hDCDest, @CX_Img, @CY_Img, @FrameWidth, @FrameHeight, @hdcSrc, @CX_Frame, @CY_Frame, @ImageWidth, @ImageHeight
Local @byPixel, @hdcImage, @lpvBits, @ImgSize, @hBmp, @DeltaY, @DeltaHeight, @MirrorFlag
Structure @BITMAP 24, @BITMAP.bmTypeDis 0, @BITMAP.bmWidthDis 4, @BITMAP.bmHeightDis 8, @BITMAP.bmWidthBytesDis 12, @BITMAP.bmPlanesDis 16,
@BITMAP.bmBitsPixelDis 18, @BITMAP.bmBitsDis 20
uses ebx, ecx, esi, edi, edx
mov esi D@pCImgStruct
If_Or D@FrameWidth = 0, D@FrameHeight = 0, D@ImageWidth = 0, D@ImageHeight = 0, D$esi+CIMAGE.m_ucpBitsDis = 0
call 'KERNEL32.SetLastError' &ERROR_INVALID_PARAMETER
xor eax eax
ExitP
End_If
; Analyse signal, set the flags acordly and then make sure all is positive
mov D@MirrorFlag 0 ; No mirror
If_And D@FrameWidth <s 0, D@ImageWidth <s 0
Else_If_Or D@FrameWidth <s 0, D@ImageWidth <s 0
inc D@MirrorFlag ; 1 = Mirror in Width (X Axis)
End_If
.If_And D@FrameHeight <s 0, D@ImageHeight <s 0
.Else_If_Or D@FrameHeight <s 0, D@ImageHeight <s 0
If D@MirrorFlag <> 0 ; Is mirror on width exist ?
mov D@MirrorFlag 3; 3 = Mirror in Width (X Axis) and Heigth (Y Axis)
Else
mov D@MirrorFlag 2; 2 = Mirror in Heigth (Y Axis)
End_If
.End_If
; make sure all is positive
ABS D@FrameWidth, 0 | mov D@FrameWidth eax
ABS D@ImageWidth, 0 | mov D@ImageWidth eax
ABS D@FrameHeight, 0 | mov D@FrameHeight eax
ABS D@ImageHeight, 0 | mov D@ImageHeight eax
;call 'gdi32.GetCurrentObject' D@hdcSrc, &OBJ_BITMAP ; USE THIS TO AVOID USING CIMAGE eax = handle to bmp hbmp
move D@hBmp D$esi+CIMAGE.m_hBmpDis
call 'RosMem.ZeroMemory' D@BITMAP, Size_Of_BITMAP
call 'GDI32.GetObjectA' D@hbmp, Size_Of_BITMAP, D@BITMAP ; get informations about the bitmap
On eax = 0, ExitP ; On error, exit
; --------- Here starts the problem !!! __--------
;sub D@CY_Img 30 ; Top of the image ?
;sub D@CY_Frame 30 ; Negative bottom of the image ?
;sub D@FrameHeight 130 ; enlarge image or shrink ?
;sub D@ImageHeight 100 ; stretchs the image up. upper left of image ?
; deltaY
mov ecx D@CY_Img | sub ecx D@CY_Frame | mov D@DeltaY ecx
mov ebx D@FrameHeight | sub ebx D@ImageHeight | mov D@DeltaHeight ebx
mov eax D@FrameHeight
...If eax > D@ImageHeight ; If destyination heitgh is bigger then original
.If D@DeltaY <s 0
; when going up
mov ebx D@CY_Frame | add D@FrameHeight ebx
mov eax D@DeltaY | add D@CY_Img eax
mov D@CY_Frame 0
mov ebx D@BITMAP.bmHeightDis | mov D@FrameHeight ebx
move D@ImageHeight D@FrameHeight;D@ImageHeight ebx
If D@DeltaHeight <> 0
mov ebx D@DeltaHeight | add D@FrameHeight ebx
End_If
.Else
; when going down or is at inside the screen
;mov D@CY_Img 0 ; top layer
;mov D@CY_Frame 0 ; top frame
; sub D@ImageHeight 30 ; bottom layer
mov eax D@DeltaHeight;D@DeltaY
mov D@FrameHeight 300
.End_If
...Else_If eax <= D@ImageHeight ; if destination height is smaller then original
.If D@DeltaY <s 0
; when going up
;mov ebx D@BITMAP.bmHeightDis | sub ebx D@ImageHeight; | shr ebx 1
move D@ImageHeight D@FrameHeight;D@BITMAP.bmHeightDis
;sub D@FrameHeight ebx
mov eax D@CY_Img ; Top of the image
mov ebx D@CY_Frame ; Negative bottom of the image ;add D@CY_Frame ebx
mov D@CY_Frame eax
mov D@CY_Img ebx
.Else
; when going down or is at inside the screen
mov ebx D@BITMAP.bmHeightDis | sub ebx D@ImageHeight | shr ebx 1
move D@ImageHeight D@BITMAP.bmHeightDis
add D@FrameHeight ebx
;add D@CY_Frame ebx
;mov D@CY_Frame 0
;add D@CY_Img 0;ebx
;mov ebx D@BITMAP.bmHeightDis | sub ebx D@FrameHeight
;add D@FrameHeight ebx
;move D@ImageHeight D@FrameHeight
;mov ebx D@DeltaHeight | add D@FrameHeight ebx
.End_If
...End_if
; -------------------------------------------------------- End of computations of cy/cx etc
move D@hBmp D$esi+CIMAGE.m_hBmpDis
call resizePixelsok D$esi+CIMAGE.m_ucpBitsDis, D@ImageWidth, D@ImageHeight, D@FrameWidth, D@FrameHeight
mov ebx eax | xor edx edx | mov edx D@FrameWidth | imul edx D@FrameHeight | shl edx 2
mov D@lpvBits ebx
mov D@ImgSize edx
call 'RosMem.ZeroMemory' OutBmpNfo, Size_Of_BITMAP
call 'GDI32.GetObjectA' D@hbmp, Size_Of_BITMAP, D@BITMAP ; get informations about the bitmap
On eax = 0, ExitP ; On error, exit
;A bit of information is necessary for GetDIBits to work. So starty filling only what is needed.
mov D$OutBmpNfo.bmiHeader.biSize Size_of_BITMAPINFO
move D$OutBmpNfo.bmiHeader.biWidth D@FrameWidth
move D$OutBmpNfo.bmiHeader.biHeight D@FrameHeight
mov eax D@BITMAP.bmPlanesDis
mov W$OutBmpNfo.bmiHeader.biPlanes ax
mov eax D@BITMAP.bmBitsPixelDis
mov W$OutBmpNfo.bmiHeader.biBitCount ax
mov D$OutBmpNfo.bmiHeader.biCompression &BI_RGB ; compression (no)
call 'GDI32.CreateCompatibleDC' &NULL | mov D@hdcImage eax
call 'GDI32.SetDIBits' eax, D@hbmp, 0, D$OutBmpNfo.bmiHeader.biHeight, D@lpvBits, OutBmpNfo, &DIB_PAL_COLORS
call 'GDI32.BitBlt' D@hdcDest, D@CX_Img, D@CY_Img, D@FrameWidth, D@FrameHeight, D@hdcSrc, D@CX_Frame, D@CY_Frame, &SRCCOPY
;;
??????
call 'GDI32.BitBlt' D@hdcDest, D@CX_Img, D@CY_Img, D@FrameWidth, D@FrameHeight, D@hdcSrc, D@CX_Frame, D@CY_Frame, &SRCCOPY
ps.rcPaint.left = D@CX_Img
ps.rcPaint.top = D@CY_Img
ps.rcPaint.right-ps.rcPaint.left = D@FrameWidth = ps.rcPaint.right - D@CX_Img
ps.rcPaint.bottom-ps.rcPaint.top = D@FrameHeight = ps.rcPaint.bottom -D@CY_Frame
ps.rcPaint.left = D@CX_Frame
ps.rcPaint.top = D@CY_Frame
, SRCCOPY
;;
call 'GDI32.DeleteDC' D@hdcImage
; release the allocated memory
call 'RosMem.VMemFree' D@lpvBits
mov eax &TRUE
EndP
The simplyfied code without the math is this:
Proc StretchBltEx:
Arguments @pCImgStruct, @hDCDest, @CX_Img, @CY_Img, @FrameWidth, @FrameHeight, @hdcSrc, @CX_Frame, @CY_Frame, @ImageWidth, @ImageHeight
Local @byPixel, @hdcImage, @lpvBits, @ImgSize, @hBmp, @DeltaY, @DeltaHeight, @MirrorFlag
Structure @BITMAP 24, @BITMAP.bmTypeDis 0, @BITMAP.bmWidthDis 4, @BITMAP.bmHeightDis 8, @BITMAP.bmWidthBytesDis 12, @BITMAP.bmPlanesDis 16,
@BITMAP.bmBitsPixelDis 18, @BITMAP.bmBitsDis 20
uses ebx, ecx, esi, edi, edx
mov esi D@pCImgStruct
If_Or D@FrameWidth = 0, D@FrameHeight = 0, D@ImageWidth = 0, D@ImageHeight = 0, D$esi+CIMAGE.m_ucpBitsDis = 0
call 'KERNEL32.SetLastError' &ERROR_INVALID_PARAMETER
xor eax eax
ExitP
End_If
; Analyse signal, set the flags acordly and then make sure all is positive
mov D@MirrorFlag 0 ; No mirror
If_And D@FrameWidth <s 0, D@ImageWidth <s 0
Else_If_Or D@FrameWidth <s 0, D@ImageWidth <s 0
inc D@MirrorFlag ; 1 = Mirror in Width (X Axis)
End_If
.If_And D@FrameHeight <s 0, D@ImageHeight <s 0
.Else_If_Or D@FrameHeight <s 0, D@ImageHeight <s 0
If D@MirrorFlag <> 0 ; Is mirror on width exist ?
mov D@MirrorFlag 3; 3 = Mirror in Width (X Axis) and Heigth (Y Axis)
Else
mov D@MirrorFlag 2; 2 = Mirror in Heigth (Y Axis)
End_If
.End_If
; make sure all is positive
ABS D@FrameWidth, 0 | mov D@FrameWidth eax
ABS D@ImageWidth, 0 | mov D@ImageWidth eax
ABS D@FrameHeight, 0 | mov D@FrameHeight eax
ABS D@ImageHeight, 0 | mov D@ImageHeight eax
;call 'gdi32.GetCurrentObject' D@hdcSrc, &OBJ_BITMAP ; USE THIS TO AVOID USING CIMAGE eax = handle to bmp hbmp
move D@hBmp D$esi+CIMAGE.m_hBmpDis
call 'RosMem.ZeroMemory' D@BITMAP, Size_Of_BITMAP
call 'GDI32.GetObjectA' D@hbmp, Size_Of_BITMAP, D@BITMAP ; get informations about the bitmap
On eax = 0, ExitP ; On error, exit
(.........Here should have some math to this work................)
move D@hBmp D$esi+CIMAGE.m_hBmpDis
call resizePixelsok D$esi+CIMAGE.m_ucpBitsDis, D@ImageWidth, D@ImageHeight, D@FrameWidth, D@FrameHeight
mov ebx eax | xor edx edx | mov edx D@FrameWidth | imul edx D@FrameHeight | shl edx 2
mov D@lpvBits ebx
mov D@ImgSize edx
call 'RosMem.ZeroMemory' OutBmpNfo, Size_Of_BITMAP
call 'GDI32.GetObjectA' D@hbmp, Size_Of_BITMAP, D@BITMAP ; get informations about the bitmap
On eax = 0, ExitP ; On error, exit
;A bit of information is necessary for GetDIBits to work. So starty filling only what is needed.
mov D$OutBmpNfo.bmiHeader.biSize Size_of_BITMAPINFO
move D$OutBmpNfo.bmiHeader.biWidth D@FrameWidth
move D$OutBmpNfo.bmiHeader.biHeight D@FrameHeight
mov eax D@BITMAP.bmPlanesDis
mov W$OutBmpNfo.bmiHeader.biPlanes ax
mov eax D@BITMAP.bmBitsPixelDis
mov W$OutBmpNfo.bmiHeader.biBitCount ax
mov D$OutBmpNfo.bmiHeader.biCompression &BI_RGB ; compression (no)
call 'GDI32.CreateCompatibleDC' &NULL | mov D@hdcImage eax
call 'GDI32.SetDIBits' eax, D@hbmp, 0, D$OutBmpNfo.bmiHeader.biHeight, D@lpvBits, OutBmpNfo, &DIB_PAL_COLORS
call 'GDI32.BitBlt' D@hdcDest, D@CX_Img, D@CY_Img, D@FrameWidth, D@FrameHeight, D@hdcSrc, D@CX_Frame, D@CY_Frame, &SRCCOPY
call 'GDI32.DeleteDC' D@hdcImage
; release the allocated memory
call 'RosMem.VMemFree' D@lpvBits
mov eax &TRUE
EndP
The images bellow shows better the problem
(http://i61.tinypic.com/2u7rm8o.jpg)
(http://i62.tinypic.com/9ztx5u.jpg)
(http://i61.tinypic.com/b9aadx.jpg)
(http://i58.tinypic.com/2ztaik3.jpg)
i really don't understand the RosAsm syntax :lol:
but, i don't see how you can use BitBlt to perform the same operation as StretchBlt
Hi Dave
thanks :)
The bitblt is used after the pixels are resized with resizePixelsok ( resizePixels in fact This i a function i made for resizing the pixels as the name suggests).
The only problem is that although the image is correctly resized, i´m failing to find the proper points of nxdest (CX_IMG) etc. I´m not being able to find the X and Y coordinates of the image ad the DC.
I´m failing to find tje corrrespondence of the arguments of both api.
Their parameters can be viewed as:
BitBlt (hdcDst, xDst, yDst, cx, cy, hdcSrc, xSrc, ySrc, dwROP) ;
StretchBlt (hdcDst, xDst, yDst, cxDst, cyDst, hdcSrc, xSrc, ySrc, cxSrc, cySrc, dwROP) ;
So, xDst, yDst, xSrc, ySrc seems to be the same for both Apis.
But, i need to find "cx, cy".
Is there some info about the relationship between those coordinates on the screen ?
I mean, cy = xyDst-cySrc ???
In the 2nd screenshot, why yDst (CY_IMG in my code) turns zeroed when the image is above the client window ? Shouldn´t it be negative ?
If i undertood how the parameters works and are related to the coordinates, it could be used to find the proper values.
Hi guga,
Haven't studied your code, but hope this helps.
Quote from: gugaI´m failing to find tje corrrespondence of the arguments of both api.
Their parameters can be viewed as:
BitBlt (hdcDst, xDst, yDst, cx, cy, hdcSrc, xSrc, ySrc, dwROP) ;
StretchBlt (hdcDst, xDst, yDst, cxDst, cyDst, hdcSrc, xSrc, ySrc, cxSrc, cySrc, dwROP) ;
So, xDst, yDst, xSrc, ySrc seems to be the same for both Apis.
But, i need to find "cx, cy".
Is there some info about the relationship between those coordinates on the screen ?
I mean, cy = xyDst-cySrc ???
- When I use these functions:
cx = cxSrc
cy = cySrc
Then after (and this is the part u might be missing?)
invoke GetWindowRect, window_handle, addr rect
cxDst = rect.right
cyDst = rect.bottom
when you resize an image, there is a good likelihood that there is no correspondance
of pixels from the original image to pixels of the resized image (see attached image)
there are a few ways to calculate the color of a pixel from the original pixels
one is the weighted average of the 1, 2, or 4 nearest pixels
the inverse-square law is used to calculate - very time consuming to do an entire image
what i have to ask is, why not just use StretchBlt ? :redface:
when using StretchBlt, i first set the mode to HALFTONE with SetStretchBltMode
(works best for most image types)
https://msdn.microsoft.com/en-us/library/dd145089%28v=vs.85%29.aspx (https://msdn.microsoft.com/en-us/library/dd145089%28v=vs.85%29.aspx)
this little program allows pan-zoom of a BMP file
(notice the trackbar controls and little buttons)
in the menus, you can choose half-tone or color-on-color modes
this demonstrates HALFTONE mode
notice the zoom control (red circles)
and, in the lower corners, buttons for status bar (S) and center-cursor (+) on/off
you can select seperate modes for reduction and enlargement
View menu - Map Inset - Zoom Modes - Enlarge/Reduce
per Edgar....
QuoteUse COLORONCOLOR when reducing.
Use HALFTONE and SetBrushOrgEx when enlarging.
I found that some image types may look best if HALFTONE is used whether enlarging or reducing.
Hi dave
Many thanks....but, i used it on the original code. The problem is that strecthblt api fails when loading huge files, such as HDR for example. It is too slow and the result is poor of the enlarging the image.
The reduce/enlarge problem i overcome with my own variation of a resizing function. The algorithm is simple to avoid speed isues when dealing with large files. The result seems a bit better then the gdi32 api. Also, internally strecth api uses bitblt already (inside win32k.sys, i mean) but before it reaches the bitblt function, it calls several other functions. So, probably it is slower then if i try to create a own variation of it.
I´ll test for speed and accuracy after i suceed to fix the Coordinates problem .
i would guess you are trying to stretch an entire image,
when only the portion that needs to be displayed should be stretched
StretchBlt is pretty fast, considering what it does
i suggest you StretchBlt the part you need into a memory DC,
then BitBlt from the memory DC into the display DC
To win time,load the image and create the DC outside WM_PAINT
StretchBlt could also be used outside WM_PAINT,and you will not see how slow it is.
the WM_PAINT must have only to put the image on screen.
Thanks guys I guess i´m suceeding to understand the math behind this api. I could be able to find the corresponding parameters of the RECT struture related to the image in the dc.
Now i´m computing the coordinates, and also it seems that i can also find the coordinates of the current working (The client area where the bitmap is located) without other apis.
A couple of things i found interesting. The stretch api returns True even when the destination and width/height have both negative values. This is weird because it should return a error.
When we have nWidthSrc and nWidthDest negative (both), it means that the bitmap is located outside the X-Axis. So, the api must return zero or another error code (Invalid parameters for example).
The same happens when nHeightDest and NHeightSrc are both negatives.
Interesting is that the msdn documentation says nothing about it. It only states that when the signs of thme are different the image is mirrored, but it says nothing when both are negative.
This info is particular usefull, when building a app with a Dc that moves along the screen (when you are moving it with the mouse, for example). Whenever both parameters are negative, the strecth api can be avoided from the computation to speed it up. Why it would speed up ? Because even thought the function returns true on this condition, it is completelly useless because the image is outside the visible area.
coordinates may be negative
it simply means there is an offset in the image location
for places where there is no image to draw, it's likely you will get black
if both width and height are negative, it is mirrored on both axis
that is the same as rotating it 180 degrees :P
there are many ways to speed up the paint code
the PAINTSTRUCT structure has a RECT inside it (rcPaint)
that RECT tells you what portion of the client area requires update
so, if you only generate the part that is needed, you can save a lot of time
what i often do, in this respect, is draw the entire client area into a memory DC
and BitBlt only the RECT required by rcPaint into the display DC
even though you may spend time drawing unnecessary image area,
drawing into a memory DC is generally very fast
drawing into a display DC is much slower
that makes the code fairly simple
HGuys
I succeded making an alternative variation of the Api (At least for Y coord). It loads, enlarge, reduce images of 12 Mb or bigger:) Btw..i´ll fix a small problem of X coord later.
But, i have a doubt on the algorithm ued by strecth api.
It seems it uses nearest neighbour or bilinear algorithms to perform the enlarging/shrinking, but when i tried to implement the nearest neighbour i´m having incorrect results.
I built an variation of the algorithm seem here
http://tech-algorithm.com/articles/nearest-neighbor-image-scaling/
public int[] resizePixels(int[] pixels,int w1,int h1,int w2,int h2) {
int[] temp = new int[w2*h2] ;
double x_ratio = w1/(double)w2 ;
double y_ratio = h1/(double)h2 ;
double px, py ;
for (int i=0;i<h2;i++) {
for (int j=0;j<w2;j++) {
px = Math.floor(j*x_ratio) ;
py = Math.floor(i*y_ratio) ;
temp[(i*w2)+j] = pixels[(int)((py*w1)+px)] ;
}
}
return temp ;
}
I ported it to RosAsm, but i added a Starting and Ending area of the inputed image to process. For example, given an image of 640*480, i would like to enlarge only a specific area of the image so the result will be a 1024*720. But, also, I would like to limit the visible area. So, if the enlarging image exceeds the area of the client window it will keep the image proportion. Example, let´s say the visible area is only 800*600. I would like to "view" a resized image from width = 640 to 1024 and height=480 to 720, but limited to a view area of 800*600 (So allowing cropping)
Example of the porting code:
YStart = 30 --> Starting at YPos 30 (CYStart)
YEnd = 290 --> Ending at YPos 290 (CYEnd)
XStart = 120 --> Starting at XPos 120 (CXStart)
XEnd = 500 --> Ending at XPos 500 (CXEnd)
SrcWidth = 640 (WidthSrc)
SrcHeight = 480 (HeightSrc)
TargetWidth = 1024 (WidthDest)
Targetheight = 720 (HeightDest)
MaxViewHeight = 800 (CYDestMax)
MaxViewWidth = 600 (CXDestMax)
Proc resizePixelsEx:
Arguments @Pixels, @CYStart, @CYEnd, @CXStart, @CXEnd, @WidthSrc, @HeightSrc, @WidthDest, @HeightDest, @CYDestMax, @CXDestMax
Local @TempMem, @iCounter, @jCounter, @X2, @Y2, @MemPix
Uses ebx, esi, edi, ecx, edx
xor eax eax
xor edx edx
mov edx D@WidthDest | imul edx D@CYDestMax | shl edx 2
mov D@MemPix 0 | lea eax D@MemPix
call 'RosMem.VMemAlloc' eax, edx
mov D@TempMem eax
fild D@WidthSrc | fidiv D@WidthDest | fstp R$x_ratio
fild D@HeightSrc | fidiv D@HeightDest | fstp R$y_ratio
mov D@iCounter 0
mov eax D@iCounter
mov esi D@Pixels
; Get Starting pos
mov ecx D@CYStart | imul ecx D@WidthSrc | add ecx D@CXStart | shl ecx 2;D@X
add esi ecx
mov edi D@TempMem
mov edx D@WidthDest | shl edx 2 | add edx esi
.While eax < D@CYDestMax;@HeightDest
;mov eax D@HeightDest
mov D@jCounter 0
mov eax D@jCounter
While eax < D@CXDestMax;@WidthDest
fild F@jCounter | fmul R$x_ratio | fistp F$CxMax
fild F@iCounter | fmul R$y_ratio | fistp F$CyMax
;;
X2 = (CyMax*Width1)+CxMax = ((iCounter*y_ratio)*Width1)+(jCounter*x_ratio)
Y2 = (iCounter*Width2)+jCounter
;;
lea eax D@X2 | fild F$CyMax | fimul F@WidthSrc | fiadd F$CxMax | fistp F$eax | mov eax D$eax
lea ecx D@Y2 | fild F@iCounter | fimul F@WidthDest | fiadd F@jCounter | fistp F$ecx | mov ecx D$ecx
mov eax D$esi+eax*4
mov D$edi+ecx*4 eax
inc D@jCounter
mov eax D@jCounter
End_While
inc D@iCounter
mov eax D@iCounter
.End_While
mov eax D@TempMem
EndP
The tests on a 4*2 image with a zoom of 196.50 gave me this result
(http://i62.tinypic.com/2dhh111.jpg)
See, the black pixels area ? This should not to be black but filled tih the proper collors as in the Original Api. I´m pretty sure the problem is with the resizing/scaling algorithm but i´m unable to find the proper solution.
The image should be a bit more to the right and a bit on tht top. Teh proportions are Ok, but the displacement of it is wrong.