Prev: Visual Basic ADODB Open Connection Make Table Query
Next: Detect Excel application minimize/restore?
From: Bee on 8 Dec 2009 19:54 I am making good progress with a picturebox lasso thanks to Mike. But I am stuck on the last issue. So far I can (1) lasso an area in a picturebox (2) create a sprite and mask (3) show the spite and mask as a moveable sprite on the picturebox (4) move the sprite and mask as desired using the mouse (5) "insert" the lassoed area into a picturebox making it part of the picture. But ... I have tried using a StretchBlt routine to flip the image. I use this routine for other non-sprite images and it works properly. I flip both the sprite and mask picturebox holders. Yes, the image does get flipped; however, there is a black area near he edges that apears. Seems that pat of the "mask" does not get "flipped". So, is there a way to flip while creating the sprite and mask or a proper way to flip afte the sprite and image are created? I think I need to flip during the sprite and mask creation. I did attempt to use the Sprite and Mask creation routine to do the flip using BitBlt. I looked over BitBlt and did not find a way according to my MSDN lib.
From: Mike Williams on 9 Dec 2009 10:46 "Bee" <Bee(a)discussions.microsoft.com> wrote in message news:330737F0-686D-46E9-8BD4-D1851EE81567(a)microsoft.com... > I have tried using a StretchBlt routine to flip the image. > I use this routine for other non-sprite images and it works > properly. I flip both the sprite and mask picturebox holders. > Yes, the image does get flipped; however, there is a black > area near he edges that apears. Seems that pat of the > "mask" does not get "flipped". StretchBlt should work fine for accurately flipping an image (whether it is the image of your sprite or your mask), and it shouldn't even matter which StretchBltMode you use as long as you are not actually stretching (reducing or increasing) the size of the image in the flip, which I assume you are not, although having said that it would probably make sense to use STRETCH_DELETESCANS (Stretch Mode 3) anyway even though you are peforming just a simple flip. > So, is there a way to flip while creating the sprite and mask or > a proper way to flip afte the sprite and image are created? > I think I need to flip during the sprite and mask creation. Flipping as part of the creation process make much more sense than flipping during an actual sprite movement routine, because StretchBlt typically takes up to three or four times as long as blitting when StretchBlt is being asked to either flip or resize the image. So if you flip during the creation of the sprite and mask then you pay that time penalty only once, and you can then use the standard Bitblt in the actual sprite movement routines. > however, there is a black area near the edges that > apears. Seems that pat of the "mask" does not > get "flipped". The StretchBlt flip should not cause that to happen, especially since you are performing a simple flip and you are not actually resizing the image as well, as long as you have specified the coordinates and sizes correctly in both cases. Mind you, having said that, there might be some problems of that nature if you are using the original version of the example code I posted, which uses regions for some of its tasks, especially if you have modified it in various ways for your own purposes. That code was just a starting example and it used a region method simply because it was the first thing that came into my head when answering your original question on this topic some time ago. I thought that I had mentioned that shortly afterwards and had posted some further example code which does not use regions, but I can't seem to find is now. Do you remember seeing that post? If not then I must have forgotten to post it, althoughy I did mean to do so and I thought I had done. Using CreatePolygonRgn to create an irregular region does not produce exactly the same shape as does drawing a filled area using the Polygon function, even if you are using exactly the same array of POINTAPI data to perform both tasks. The shape drawn by Polygon will be very slightly larger than the shape created by CreatePolygonRgn, although it is about a pixel larger in a slightly irregular fashion. That is because the various region functions in general behave slightly differently than do the various drawing methods, especially in relation to the rightmost and bottommost end points of the line segments. Here's some example code which does not use regions in the creation of the hand drawn area, or anywhere else, and which allows you to manually draw an irregular shape using the mouse. When you release the mouse button the code closes the shape (if it is not already closed) and it creates a sprite and a mask of that shape and positions it at its initial display position, drawing a "marching ants" outline around the sprite so that you can easily see it on the main picture. You can then drag the sprite around the screen using the mouse. Clicking anywhere other than on the sprite itself then drops the sprite at its last position, allowing you to draw another shape. Pressing the Ctrl key on the keyboard toggles between "normal" and "flip" mode (which in this example applies to the next sprite you draw and not to any sprite you may already be dragging around, although of course that kind of code could be added if you wish). In flip mode the sprite and mask are created as normal when you release the mouse button after drawing, but both the sprite image and the mask image are then flipped in the x direction (using StretchBlt) before being plotted on the display. I've added the "flip the sprite" code rather hurriedly in response to this latest post of yours, and so I haven't yet had time to flip the "marching ants" outline as well (which requires a little more thought), so in flip mode the marching ants outline does not correctly surround the shape of the sprite, but you should nevertheless be able to see that StretchBlt does in fact perform the flip accurately, both in the case of the sprite and he mask. The code is rather "rambling" and it could really do with a re-write in a more logical fashion, but it should be enough to help you with your sprite flipping problem. Start a new VB project and place four PictureBoxes and one Timer Control on the Form and then name the PictureBoxes picMain, picSprite, picMask and picBackBuffer (I've chosen a separate backbuffer for this example, from which I can pull the small sections to clear the old sprite area, although there are various other ways of doing it of course). Paste in the following code and change the hard coded picture path to a picture that exists on your own system. Mike Option Explicit Private Declare Function StretchBlt Lib "gdi32" _ (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 Declare Function SetStretchBltMode Lib "gdi32" _ (ByVal hdc As Long, ByVal nStretchMode As Long) As Long Private Declare Function Polygon Lib "gdi32" _ (ByVal hdc As Long, lpPoint As POINTAPI, _ ByVal nCount As Long) As Long Private Declare Function BitBlt Lib "gdi32" _ (ByVal hDestDC 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 dwRop As Long) As Long Private Declare Function SetViewportOrgEx Lib "gdi32" _ (ByVal hdc As Long, ByVal nX As Long, _ ByVal nY As Long, lpPoint As POINTAPI) As Long Private Declare Function SetPolyFillMode Lib "gdi32" _ (ByVal hdc As Long, ByVal nPolyFillMode As Long) As Long Private Const COLORONCOLOR = 3 Private Const ALTERNATE As Long = 1 Private Const WINDING As Long = 2 Private Type RECT Left As Long Top As Long Right As Long Bottom As Long End Type Private Type POINTAPI x As Long y As Long End Type Private d1() As POINTAPI, lastCoord As Long Private mainWidth As Long, mainHeight As Long Private xMin As Long, xMax As Long, yMin As Long, yMax As Long Private SpriteX As Long, SpriteY As Long Private spriteWide As Long, spriteHigh As Long Private SpriteDisplayed As Boolean, Drawing As Boolean Private MouseInSprite As Boolean, Dragging As Boolean Private offsetX As Long, offsetY As Long Private flipX As Boolean Private Sub Form_KeyUp(KeyCode As Integer, Shift As Integer) If KeyCode = vbKeyControl Then flipX = Not flipX DisplayFlipStatus End If End Sub Private Sub DisplayFlipStatus() If flipX Then Me.Caption = "Next selection will be flipped" Else Me.Caption = "Next selection will NOT be flipped" End If Me.Caption = Me.Caption & " (Press Ctrl to change)" End Sub Private Sub Form_Load() Dim s1 As String Me.WindowState = vbMaximized Me.Show s1 = "c:\temp\jessica1.jpg" InitPicBox picMain, True InitPicBox picBackBuffer, False InitPicBox picSprite, False InitPicBox picMask, False picMain.Move 0, 0, Me.ScaleWidth, Me.ScaleHeight picBackBuffer.Move 0, 0, picMain.Width, picMain.Height mainWidth = picMain.ScaleWidth mainHeight = picMain.ScaleHeight picMain.PaintPicture LoadPicture(s1), 0, 0, _ mainWidth, mainHeight BitBlt picBackBuffer.hdc, 0, 0, mainWidth, mainHeight, _ picMain.hdc, 0, 0, vbSrcCopy picMask.BackColor = vbWhite picMask.FillColor = vbBlack picMask.FillStyle = vbFSSolid SetStretchBltMode picMain.hdc, COLORONCOLOR DisplayFlipStatus Me.KeyPreview = True Timer1.Interval = 200 Timer1.Enabled = True End Sub Private Sub InitPicBox(p1 As PictureBox, SetVisible As Boolean) p1.Visible = SetVisible p1.BorderStyle = vbBSNone p1.ScaleMode = vbPixels p1.AutoRedraw = True End Sub Private Sub picMain_DblClick() ' A double click inside the area of a sprite indicates ' that user has finished dragging and wants to drop the ' sprite where it is. If MouseInSprite Then DropSprite SpriteX, SpriteY End If End Sub Private Sub picmain_MouseDown(Button As Integer, _ Shift As Integer, x As Single, y As Single) 'If Button <> vbLeftButton Then Exit Sub If MouseInSprite Then ' User wants to start dragging sprite offsetX = SpriteX - x offsetY = SpriteY - y Dragging = True 'picMain.DrawStyle = vbDot Else ' A click outside the sprite indicates user has finished ' dragging and possibly also wants to start drawing a new ' selection area, so we need to drop the sprite at its ' current location and then set up the conditions for ' a new drawing If Button = vbLeftButton Then ' drop sprite at current position DropSprite SpriteX, SpriteY ' start a new drawing Drawing = True picMain.DrawMode = vbNop picMain.DrawStyle = vbSolid picMain.PSet (x, y) picMain.DrawMode = vbInvert ReDim d1(0 To 100) lastCoord = 0 d1(lastCoord).x = x d1(lastCoord).y = y xMin = x: xMax = x yMin = y: yMax = y Else ' cancel the operation (remove sprite from picMain) DropSprite -spriteWide, -spriteHigh End If End If End Sub Private Sub picmain_MouseMove(Button As Integer, _ Shift As Integer, x As Single, y As Single) If x >= mainWidth Then x = mainWidth - 1 If y >= mainHeight Then y = mainHeight - 1 If x < 0 Then x = 0 If y < 0 Then y = 0 If Drawing Then If x > xMax Then xMax = x If x < xMin Then xMin = x If y > yMax Then yMax = y If y < yMin Then yMin = y picMain.Line -(x, y) lastCoord = lastCoord + 1 If lastCoord > UBound(d1) Then ReDim Preserve d1(0 To lastCoord + 100) End If d1(lastCoord).x = x d1(lastCoord).y = y End If If Dragging And Button = vbLeftButton Then DisplaySprite CLng(x) + offsetX, CLng(y) + offsetY, True End If MouseInSprite = SpriteDisplayed And _ (picMask.Point(x - SpriteX, y - SpriteY) = 0) If MouseInSprite Then If picMain.MousePointer <> 15 Then picMain.MousePointer = 15 End If Else If picMain.MousePointer <> 0 Then picMain.MousePointer = 0 End If End If End Sub Private Sub picmain_MouseUp(Button As Integer, _ Shift As Integer, x As Single, y As Single) If Button <> vbLeftButton Then Exit Sub If Drawing Then ' close the shape and create the sprite and mask picMain.Line -(d1(0).x, d1(0).y) spriteWide = xMax - xMin + 1 spriteHigh = yMax - yMin + 1 If lastCoord > 0 Then CreateAndDisplaySprite End If Drawing = False End If End Sub Private Sub MakeSpriteAndMask() Dim oldOrg As POINTAPI picSprite.Width = Me.ScaleX(spriteWide, vbPixels, Me.ScaleMode) picSprite.Height = Me.ScaleY(spriteHigh, vbPixels, Me.ScaleMode) Me.ScaleMode = vbPixels ' just for test purposes for the following line ' create black on white picMask picMask.Width = picSprite.Width picMask.Height = picSprite.Height picMask.Cls ' clear mask to white ' Draw black on white mask to picMask. ' First offset the origin of picMask so that we ' can use the same array of point data to draw ' a copy of the polygon at the top left of the ' Mask picbox. SetViewportOrgEx picMask.hdc, -xMin, -yMin, oldOrg ' Then draw the polygon SetPolyFillMode picMask.hdc, WINDING Polygon picMask.hdc, d1(0), lastCoord + 1 ' Then set the origin back to normal, otherwise VB ' will become very confused when we use any native ' VB methods on the picturebox. SetViewportOrgEx picMask.hdc, oldOrg.x, oldOrg.y, oldOrg ' blit the full rectangle of the selection to picSprite BitBlt picSprite.hdc, 0, 0, spriteWide, _ spriteHigh, picMain.hdc, _ xMin, yMin, vbSrcCopy ' Now OR the mask to it to create a standard ' "image on white" sprite image BitBlt picSprite.hdc, 0, 0, spriteWide, _ spriteHigh, picMask.hdc, _ 0, 0, vbSrcPaint If flipX = True Then ' flip both sprite and mask StretchBlt picMask.hdc, spriteWide, 0, -spriteWide, _ spriteHigh, picMask.hdc, _ 0, 0, spriteWide, spriteHigh, vbSrcCopy StretchBlt picSprite.hdc, spriteWide, 0, -spriteWide, _ spriteHigh, picSprite.hdc, _ 0, 0, spriteWide, spriteHigh, vbSrcCopy End If End Sub Private Sub CreateAndDisplaySprite() ' temporarily erase vbinvert drawn polygon by redrawing Polygon picMain.hdc, d1(0), lastCoord + 1 ' don't bother with extremely small sprites If spriteWide > 2 And spriteHigh > 2 Then ' call the routine to make the sprite and mask MakeSpriteAndMask SpriteX = xMin: SpriteY = yMin ' initial sprite position DisplaySprite SpriteX, SpriteY, True ' (True = include outline) SpriteDisplayed = True End If End Sub Private Sub DisplaySprite(x As Long, y As Long, AddOutline As Boolean) ' The method used here eliminates flicker when dragging ' or animating the sprite by having the display PictureBox ' Autoredraw True. There are other ways of doing it if you ' are running a very slow machine at a very high display ' resolution. Dim orgOld As POINTAPI ' first redraw original background at old sprite position BitBlt picMain.hdc, SpriteX, SpriteY, _ spriteWide, spriteHigh, picBackBuffer.hdc, _ SpriteX, SpriteY, vbSrcCopy ' now transparently draw the sprite at its new position BitBlt picMain.hdc, x, y, spriteWide, spriteHigh, _ picMask.hdc, 0, 0, vbMergePaint BitBlt picMain.hdc, x, y, spriteWide, spriteHigh, _ picSprite.hdc, 0, 0, vbSrcAnd If AddOutline Then SetViewportOrgEx picMain.hdc, x - xMin, y - yMin, orgOld picMain.DrawStyle = vbDot Polygon picMain.hdc, d1(0), lastCoord + 1 SetViewportOrgEx picMain.hdc, orgOld.x, orgOld.y, orgOld picMain.DrawStyle = vbSolid End If picMain.Refresh ' update SpriteX and SpriteY variables to new coordinates SpriteX = x SpriteY = y End Sub Private Sub DropSprite(x As Long, y As Long) DisplaySprite x, y, False ' display without outline ' copy modified main image to buffer BitBlt picBackBuffer.hdc, 0, 0, mainWidth, mainHeight, _ picMain.hdc, 0, 0, vbSrcCopy SpriteDisplayed = False MouseInSprite = False Dragging = False If picMain.MousePointer <> 0 Then picMain.MousePointer = 0 End If End Sub Private Sub Timer1_Timer() ' draw "marching ants" outline by repeatedly drawing ' a solid/invert polygon over an initially drawn ' dotted/invert polygon. Dim orgOld As POINTAPI If SpriteDisplayed Then ' setting Autoredraw to False whilst drawing the Polygon ' means we don't need to issue a PicBox.Refresh in order ' for the drawing to be seen, saving time overall, whilst ' maintaing Autoredraw True for other purposes picMain.AutoRedraw = False ' offset viewport so we can use the same original polygon ' data array at any other position we wish SetViewportOrgEx picMain.hdc, SpriteX - xMin, _ SpriteY - yMin, orgOld Polygon picMain.hdc, d1(0), lastCoord + 1 ' set viewport back to normal or VB will become confused SetViewportOrgEx picMain.hdc, orgOld.x, orgOld.y, orgOld picMain.AutoRedraw = True End If End Sub
From: Bee on 9 Dec 2009 11:34 That is a lot to study, but I will. I really enjoy just learning new things. Thanks for all your help. My newsreader is messed up again (works, then stops working compaining that the ISP has rejected me just after successfullly posting a few minutes earlier) and MS communities is not working so I am not sure where this post will wind up. "Bee" wrote: > I am making good progress with a picturebox lasso thanks to Mike. > But I am stuck on the last issue. > So far I can > (1) lasso an area in a picturebox > (2) create a sprite and mask > (3) show the spite and mask as a moveable sprite on the picturebox > (4) move the sprite and mask as desired using the mouse > (5) "insert" the lassoed area into a picturebox making it part of the picture. > > But ... > I have tried using a StretchBlt routine to flip the image. > I use this routine for other non-sprite images and it works properly. > I flip both the sprite and mask picturebox holders. > Yes, the image does get flipped; however, there is a black area near he > edges that apears. Seems that pat of the "mask" does not get "flipped". > So, is there a way to flip while creating the sprite and mask or a proper > way to flip afte the sprite and image are created? > I think I need to flip during the sprite and mask creation. > I did attempt to use the Sprite and Mask creation routine to do the flip > using BitBlt. > I looked over BitBlt and did not find a way according to my MSDN lib. > >
From: Bee on 9 Dec 2009 18:30 Seems to have some code missing Mike. Studying what I have. "Bee" wrote: > I am making good progress with a picturebox lasso thanks to Mike. > But I am stuck on the last issue. > So far I can > (1) lasso an area in a picturebox > (2) create a sprite and mask > (3) show the spite and mask as a moveable sprite on the picturebox > (4) move the sprite and mask as desired using the mouse > (5) "insert" the lassoed area into a picturebox making it part of the picture. > > But ... > I have tried using a StretchBlt routine to flip the image. > I use this routine for other non-sprite images and it works properly. > I flip both the sprite and mask picturebox holders. > Yes, the image does get flipped; however, there is a black area near he > edges that apears. Seems that pat of the "mask" does not get "flipped". > So, is there a way to flip while creating the sprite and mask or a proper > way to flip afte the sprite and image are created? > I think I need to flip during the sprite and mask creation. > I did attempt to use the Sprite and Mask creation routine to do the flip > using BitBlt. > I looked over BitBlt and did not find a way according to my MSDN lib. > >
From: Mike Williams on 10 Dec 2009 01:54 "Bee" <Bee(a)discussions.microsoft.com> wrote in message news:1963717C-1E7A-4EAD-97E7-E2CBD4F5842D(a)microsoft.com... > Seems to have some code missing Mike. > Studying what I have . . . > [Lots of stuff snipped] I'm not sure what you mean, Bee? What code are you saying is missing? Or have I misunderstood what you have said, either now or in your previous posts? The example code I posted does all of the things you mention, including the facility to correctly and accurately flip the sprite and mask images (using StretchBlt) at the time you create it. All you need to do is create a new VB Project and place a Timer and four PictureBoxes on the Form, naming them as described in my previous post, and then paste in the example code I posted, changing the hard coded picture path/name to a picture that exists on your own system. When you run the code you should be able to use the mouse to manually draw an irregular selection area and when you release the mouse button that area becomes a sprite, with a "marching ants" outline so that you can see it (if it were not for the marching ants outline then you would not initially be able to see the sprite because it is initially placed directly over the area of the background picture from which it was created). Then you should be able to click anywhere within the sprite and drag it around, repeatedly moving it as many times as you wish before finally clicking an area of the main picture other than the sprite itself, which will cause the sprite to be "dropped" at its current location and thereafter become part of the background image. You can then manually create another sprite, using the same procedure. Also, the Ctrl key on the keyboard toggles between "normal sprite" and "flipped sprite (x flip)", with the current state reported in the Form's caption bar. If the current state is "flipped" (before you start manually drawing an irregular selection area) then the resultant sprite when you release the mouse button to create it will be a "flipped" sprite (x flipped in the example code). Is that not what you wanted? Or is that not what you meant by "flipped"? Mike
|
Next
|
Last
Pages: 1 2 Prev: Visual Basic ADODB Open Connection Make Table Query Next: Detect Excel application minimize/restore? |