反向FloodFill:测试点是否在区域内

时间:2014-09-09 14:29:41

标签: vb6 gdi flood-fill

我有一张带有大图片的图片框:

enter image description here

在此图片框中,使用ExtFloodFill() API

对各个区域进行着色
Private Declare Function ExtFloodFill Lib "GDI32" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, ByVal crColor As Long, ByVal wFillType As Long) As Long

启动FloodFill的X和Y坐标位于必须填充的区域中的某个位置,但不在精确的中心,或者对于所有区域具有相同的偏移,并且区域的形状或尺寸。 (这就是我们喜欢FloodFill的原因)

我现在希望用户通过点击它并使用与他们点击的区域相对应的特定操作的坐标来与图片进行交互。

为此我使用_MouseDown()事件:

Private Sub picfat_MouseDown(Button As Integer, Shift As Integer, x As Single, y As Single)
  Dim lngIndex As Long
  lngIndex = CheckClick(mobjDet, x, y)
  If lngIndex > -1 Then
    With mobjDet(lngIndex)
      .lngStat = 2 - .lngStat
      SendCmd "det swico " & CStr(lngIndex) & "=" & CStr(.lngStat), wskDet
    End With 'mobjDet(lngIndex)
  End If
End Sub

调用CheckClick()来确定点击所在的区域:

Private Function CheckClick(obj() As FC_DET, sngX As Single, sngY As Single) As Long
  Dim lngObj As Long
  Dim lngIndex As Long
  Dim sngWidth As Single, sngHeight As Single
  sngWidth = 15
  sngHeight = 15
  lngIndex = -1
  For lngObj = 0 To UBound(obj)
    With obj(lngObj)
      If sngWidth > 0 Then
        If sngX > .sngX Then
          If sngX < .sngX + sngWidth Then
            If sngY > .sngY Then
              If sngY < .sngY + sngHeight Then
                lngIndex = .lngIndex
                Exit For
              End If
            End If
          End If
        End If
      End If
    End With 'obj(lngObj)
  Next lngObj
  CheckClick = lngIndex
End Function

此时我在原始X和Y坐标的右下角使用15x15像素的正方形,如果用户点击这些坐标,它将正常工作。

如果FloodFill的X和Y坐标是小方块的左上角,则15x15方格适用于图片中的较小方块,但情况并非如此,并且您可以看到还有其他形状以及小方块。

我想做的是:

  • 使用_MouseDown()事件
  • 中的X和Y坐标
  • 循环浏览FloodFill中的X和Y坐标列表
  • 确定哪个FloodFill开始对应于MouseDown事件的X和Y坐标

[编辑]

例如:

  • 我照片中黄色填充物的中心是500,160(白色正方形和矩形之间的黄色)。其坐标为mobjDet(6).sngXmobjDet(6).sngY
  • 用户点击白色矩形上方黄色区域的某处,例如495,53
  • 如何将_MouseDown()事件的X和Y与FloodFill udt的索引(6)相结合?

这样我就可以找出用户在图片中点击了哪个区域。

3 个答案:

答案 0 :(得分:2)

当我过去做过这样的事情时,最适合我的方法是创建一个UDT数组。每个UDT都拥有调用我的绘制API(如FloodFill)所需的所有信息(例如colorx1x2y1y2)。当我不得不渲染我的屏幕时,我只是在我的阵列中循环,画出了我的所有物品。当用户点击我的hdc时,我会再次循环以使用相同的坐标找到匹配的UDT,其中还包含其他信息,如id等。

Private Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type

Private Type DrawItem
    Id As Long
    Name As String
    ' etc...

    tRect As RECT
End Type

Private DrawItems() As DrawItem

此外,由于FloodFill几乎不可能跟踪填充的内容,因此我建议您使用更加笨拙的API(如FillRect)手动绘制图形。 这意味着您将拥有在UDT 中绘制的全部边界(顶部,底部,左侧和右侧),以便您可以查看是否有鼠标点击是在那个矩形内。

这是用于绘制矩形的API。你还需要创建一个&#34;画笔&#34;使用CreateSolidBrush函数。

Private Declare Function FillRect Lib "user32" (ByVal hdc As Long, lpRect As RECT, ByVal hBrush As Long) As Long
Private Declare Function CreateSolidBrush Lib "gdi32" (ByVal crColor As Long) As Long

另外,我喜欢将我正在绘制的任何内容的ScaleMode(例如,PictureBox)从1 - Twip(无用)更改为3 - Pixel所以我不会经常拥有乘以或除以15。

希望这有帮助。


最后请注意,如果您真的与FloodFill挂钩,我建议的最好的就是使用......

Private Declare Function GetPixel Lib "gdi32" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long) As Long

...可以在鼠标点击x和y坐标时返回像素的颜色。然后你可以测试颜色(希望)匹配你需要的颜色。

答案 1 :(得分:1)

使用GDI,您可以使用Regions。这些将要求您在代码中定义区域(或除位图之外的其他结构,可能是点轮廓数组),但允许您:

  1. 使用CreatePolygonRgn()CreatePolyPolygonRgn()
  2. 创建多边形区域
  3. 使用FrameRgn()
  4. 绘制边框
  5. 使用FillRgn()
  6. 填充该地区
  7. 点击测试点击,查看他们使用PtInRgn()
  8. 所在的区域

答案 2 :(得分:1)

感谢LimaNightHawk和Deanna让我知道如何解决这个问题:)

在我的图片框中加载位图后,我在内存中复制了它。 在该副本中,我然后用自己的颜色填充每个重要区域:

Private Sub CopyView()
  Dim lngIndex As Long
  Dim sngWidth As Single, sngHeight As Single
  Dim lngBrush As Long
  Dim lngBmp As Long
  sngWidth = ScaleWidth
  sngHeight = ScaleHeight
  lngBrush = CreateSolidBrush(vbYellow)
  With picFAT
    mlngCopy = CreateCompatibleDC(.hdc)
    SelectObject mlngCopy, lngBrush
    If mlngCopy <> 0 Then
      lngBmp = CreateCompatibleBitmap(.hdc, sngWidth, sngHeight)
      If lngBmp <> 0 Then
        SelectObject mlngCopy, lngBmp
        BitBlt mlngCopy, 0, 0, sngWidth, sngHeight, .hdc, 0, 0, SRCCOPY
        For lngIndex = 0 To UBound(mobjDet)
          With mobjDet(lngIndex)
            lngBrush = CreateSolidBrush(lngIndex + 1)
            SelectObject mlngCopy, lngBrush
            ExtFloodFill mlngCopy, .sngX, .sngY, GetPixel(mlngCopy, .sngX, .sngY), 1
          End With 'mobjDet(lngIndex)
        Next lngIndex
      End If
    End If
  End With 'picFAT
  DeleteObject lngBrush
End Sub

为了使它非常简单,我使用区域的索引号作为颜色。

然后,我可以通过读取内存副本中该区域的颜色来获取索引:

Private Function FindIndex(sngX As Single, sngY As Single) As Long
  Dim lngColor As Long
  lngColor = GetPixel(mlngCopy, sngX, sngY)
  FindIndex = lngColor - 1
End Function

最后我从MouseDown事件调用上面的内容来使用用户点击的区域的索引:

Private Sub picfat_MouseDown(Button As Integer, Shift As Integer, x As Single, y As Single)
  Dim lngIndex As Long
  lngIndex = FindIndex(x, y)
  If lngIndex > -1 Then
    If lngIndex <= UBound(mobjDet) Then
      With mobjDet(lngIndex)
        .lngStat = 2 - .lngStat
        SendCmd "det swico " & CStr(.lngIndex) & "=" & CStr(.lngStat), wskDet
      End With 'mobjDet(lngIndex)
    End If
  End If
End Sub

请参阅下面的API声明:

'API
Private Const SRCCOPY = &HCC0020

Private Declare Function ExtFloodFill Lib "gdi32" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, ByVal crColor As Long, ByVal wFillType As Long) As Long
Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function CreateCompatibleBitmap Lib "gdi32" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Private Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject 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 GetPixel Lib "gdi32" (ByVal hdc As Long, ByVal x As Long, ByVal y As Long) As Long
Private Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long
Private Declare Function CreateSolidBrush Lib "gdi32" (ByVal crColor As Long) As Long