[RESOLVED] StretchBlt to update a part of the image
I have a full screen image size : 1366x768 that scaled to a picture box roughly 847x476 .
Afterwards, only a few dozens of pixel changed on the top-left of the original 1366x768 image and I want to update that changes on the scaled image.
As usual, the easiest way is to do a full stretch:
Code:
StretchBlt(hdc2, 0, 0, 847, 476, hdc1, 0, 0, 1366, 768, ...)
However, a full stretch will earn a lot of performance, since i just want to update only a part of the scaled image.
So I tried to stretch only a part of the image, for example a small part about 80x80 pixel on the top left:
srcX=0; targetX =0
srcY=0; targetY =0
srcWidth = 80; targetWidth = (847/1366) * 80= 49.6046852 = ~50 pixel
srcHeight = 80; targetHeight = (476/768) * 80=49.58333 = ~50 pixel
Code:
StretchBlt(hdc2, 0, 0, 50, 50, hdc1, 0, 0, 80, 80, ...)
However, after doing it, I realized that there is a distortion on the scaled part that does not fit with the full stretched image. I think it caused because the portion is not an integer. Is there any idea about how to fix this issue :confused::confused:
Re: StretchBlt to update a part of the image
I haven't tried anything, but I think I would try to make the scaling as symmetrical as possible to the original image size.
So if your source is 1366x768, and you want the size to be close to 847x476, I would try using 854x480 as the scaled version.
The 768 to 480 conversion should scale on a pixel boundary (I'm theorizing, not tested). And an 80x80 portion of the scaled image should also correspond to an integer pixel boundary (128x128) of the original image. I would think this would minimize the distortion from stretching. The horizontal resolution 1366 to 854 is still not symmetrical with the vertical stretch (768 to 480) but since the portions of the stretch (128x128 to 80x80) are, then perhaps the edge distortion of the stretch will match at those values. (i.e. if you stretch subsections of your large image to your smaller image in "tile" sizes of 128x128 from the original to 80x80 tiles on the scaled image, maybe the stretch compromises will align).
This is all mathematical theory with no empirical testing to back it up, but may be worth a shot.
Re: StretchBlt to update a part of the image
If you set a clipping rectangle (stretched size) to encompass the target area (+1 pixel in width/height) then GDI should only try to render the clipping area not entire image. The tricky part is calculating the starting top/left target pixel to begin the rendering. However, not guaranteed to prevent distortion around the edges.
Re: StretchBlt to update a part of the image
Thank you all for reply on my thread, after a dozen times tested. I realized that the only way to make no distortion is to make an integer scale. For example, (847/1366)= 0.62005.... is an odd number so this is a problem. Try to using other portion that does not produce an odd value will resolve this, but it hard to achieve.
1 Attachment(s)
Re: StretchBlt to update a part of the image
If you are not willing to give in just yet, try this out and let us know if it is what you are after? Maybe it needs a off-by-one pixel adjustment if it is close, but it appears to work. Apologize for the rough code, threw it together quickly.
1. Create a new test project. On the form add 2 picturebox controls and 1 command button. Leave their default names
2. In Picture1, add the attached bitmap
3. Add this code to the form, run the project and test
Code:
Option Explicit
Private Declare Function StretchBlt Lib "gdi32.dll" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal nSrcWidth As Long, ByVal nSrcHeight As Long, ByVal dwRop As Long) As Long
Private Sub Command1_Click()
Dim srcX As Long, srcY As Long
Dim srcCx As Long, srcCy As Long
Dim destX As Long, destY As Long
Dim destCx As Long, destCy As Long
Dim sclX As Single, sclY As Single
Dim x As Single, y As Single, lColor As Long
srcY = Picture1.ScaleHeight \ 4
srcX = Picture1.ScaleWidth \ 4
For y = srcY To Picture1.ScaleHeight - Picture1.ScaleHeight \ 4
For x = srcX To Picture1.ScaleWidth - Picture1.ScaleWidth \ 4
lColor = Picture1.Point(x, y)
Picture1.PSet (x, y), lColor Xor &HFFFFFF
Next
Next
srcCy = y - srcY
srcCx = x - srcX
sclX = Picture2.ScaleWidth / Picture1.ScaleWidth
sclY = Picture2.ScaleHeight / Picture1.ScaleHeight
destX = srcX * sclX
destY = srcY * sclY
destCx = srcCx * sclX
destCy = srcCy * sclY
StretchBlt Picture2.hdc, destX, destY, destCx, destCy, _
Picture1.hdc, srcX, srcY, srcCx, srcCy, vbSrcCopy
Picture2.Refresh
End Sub
Private Sub Form_Load()
With Picture1
.AutoRedraw = True: .AutoSize = True
.ScaleMode = vbPixels
.Move 3210, 150
End With
With Picture2
.AutoRedraw = True
.ScaleMode = vbPixels ' on next line, change scale from 0.62005as desired:
.Move 270, 150, Picture1.Width * 0.62005, Picture1.Height * 0.62005
End With
With Command1
.Caption = "Toggle Inverse"
.Move 3870, 3465, 2100, 525
End With
StretchBlt Picture2.hdc, 0, 0, Picture2.ScaleWidth, Picture2.ScaleHeight, _
Picture1.hdc, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, vbSrcCopy
End Sub
Edited: Resize Picture2 as desired to produce other scale ratios. You'll want to remove the lines of code in Form_Load that position/size the controls if you are manually placing them on the form where you want them. You can modify the For:Next loop to select different areas of the source bitmap to invert. If this works for you, you wouldn't need to consider a clipping rectangle either.
The test project scales from Picture1 to Picture2 (downward scale). I think you are scaling down also? If not, the logic remains, but the source & destination are swapped within the logic
Re: StretchBlt to update a part of the image
Hi LaVolpe,
Nice to talk with you and thank you very much for trying to help me.
To see my problem clearly , let add a timer, named timer1 to the form, and edit a bit from your code :
Code:
Option Explicit
Private Declare Function StretchBlt Lib "gdi32.dll" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal nSrcWidth As Long, ByVal nSrcHeight As Long, ByVal dwRop As Long) As Long
Dim toggled As Boolean
Private Sub Command1_Click()
Dim srcX As Long, srcY As Long
Dim srcCx As Long, srcCy As Long
Dim destX As Long, destY As Long
Dim destCx As Long, destCy As Long
Dim sclX As Single, sclY As Single
Dim x As Single, y As Single, lColor As Long
srcY = Picture1.ScaleHeight \ 4
srcX = Picture1.ScaleWidth \ 4
' We don't need this anymore:
'For y = srcY To Picture1.ScaleHeight - Picture1.ScaleHeight \ 4
' For x = srcX To Picture1.ScaleWidth - Picture1.ScaleWidth \ 4
' lColor = Picture1.Point(x, y)
' Picture1.PSet (x, y), lColor Xor &HFFFFFF
' Next
'Next
' Replace with this code:
y = Picture1.ScaleHeight - Picture1.ScaleHeight \ 4
x = Picture1.ScaleWidth - Picture1.ScaleWidth \ 4
srcCy = y - srcY
srcCx = x - srcX
sclX = Picture2.ScaleWidth / Picture1.ScaleWidth
sclY = Picture2.ScaleHeight / Picture1.ScaleHeight
destX = srcX * sclX
destY = srcY * sclY
destCx = srcCx * sclX
destCy = srcCy * sclY
StretchBlt Picture2.hdc, destX, destY, destCx, destCy, _
Picture1.hdc, srcX, srcY, srcCx, srcCy, vbSrcCopy
Picture2.Refresh
End Sub
Private Sub Form_Load()
Picture1.Picture = LoadPicture("C:\fullscreentest.bmp") ' a full screen image captured, with text content at the center screen to see the effect more clearly
Timer1.interval =300
Timer1.Enabled= true
With Picture1
.AutoRedraw = True: .AutoSize = True
.ScaleMode = vbPixels
.Move 3210, 150
End With
With Picture2
.AutoRedraw = True
.ScaleMode = vbPixels ' on next line, change scale from 0.62005as desired:
.Move 270, 150, Picture1.Width * 0.62005, Picture1.Height * 0.62005
End With
With Command1
.Caption = "Toggle Inverse"
.Move 3870, 3465, 2100, 525
End With
StretchBlt Picture2.hdc, 0, 0, Picture2.ScaleWidth, Picture2.ScaleHeight, _
Picture1.hdc, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, vbSrcCopy
End Sub
Private Sub Timer1_Timer()
If toggled = True Then
StretchBlt Picture2.hdc, 0, 0, Picture2.ScaleWidth, Picture2.ScaleHeight, _
Picture1.hdc, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, vbSrcCopy
Picture2.Refresh
toggled = False
Else
toggled = True
Command1_Click
End If
End Sub
Now press F5 to run, you will see the scaled bitmap 'blinking' at the center and a distortion appear around & inside the scaled region, make it very easy to notice. All i want is to make no blinking and no distortion, but it seems hard to achieve.
Re: StretchBlt to update a part of the image
I see what you are describing. Well, maybe a clipping region on the destination might prove successful. You will be doing a full stretchblt, but the clipping area should prevent the entire destination from being updated...
Code:
' APIs
Private Declare Function CreateRectRgn Lib "gdi32.dll" (ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long
Private Declare Function DeleteObject Lib "gdi32.dll" (ByVal hObject As Long) As Long
Private Declare Function SelectClipRgn Lib "gdi32.dll" (ByVal hDC As Long, ByVal hRgn As Long) As Long
' sample adjustment
Dim hRgn As Long
hRgn = CreateRectRgn(destX, destY, destCx + destX, destCy + destY)
SelectClipRgn Picture2.hdc, hRgn
DeleteObject hRgn
StretchBlt Picture2.hdc, 0, 0, Picture2.ScaleWidth, Picture2.ScaleHeight, _
Picture1.hdc, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, vbSrcCopy
SelectClipRgn Picture2.hdc, 0
Re: StretchBlt to update a part of the image
Quote:
Originally Posted by
LaVolpe
I see what you are describing. Well, maybe a clipping region on the destination might prove successful. You will be doing a full stretchblt, but the clipping area should prevent the entire destination from being updated...
Code:
' APIs
Private Declare Function CreateRectRgn Lib "gdi32.dll" (ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long
Private Declare Function DeleteObject Lib "gdi32.dll" (ByVal hObject As Long) As Long
Private Declare Function SelectClipRgn Lib "gdi32.dll" (ByVal hDC As Long, ByVal hRgn As Long) As Long
' sample adjustment
Dim hRgn As Long
hRgn = CreateRectRgn(destX, destY, destCx + destX, destCy + destY)
SelectClipRgn Picture2.hdc, hRgn
DeleteObject hRgn
StretchBlt Picture2.hdc, 0, 0, Picture2.ScaleWidth, Picture2.ScaleHeight, _
Picture1.hdc, 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, vbSrcCopy
SelectClipRgn Picture2.hdc, 0
Yessssssss, it workingggg! The performance increased quite a lot ! Thank you very much Lalvope. You're amazingg!
( Incredible! I was thought this issue won't able to resolve. I'm very happy to hear your response! Thank you again thousand times! )