function DBGridVisibleDataRows(AGrid: TDBGrid): Integer;
var
  LTemTitulo         : Boolean;
  LLinhaBase         : Integer;
  r0, r1           : TRect;
  rowH, topData,
  visH             : Integer;
begin
  if (AGrid = nil) or (AGrid.ClientHeight <= 0) then
    Exit(0);

  LTemTitulo := dgTitles in AGrid.Options;
  LLinhaBase  := Ord(LTemTitulo);        // 0 sem títulos, 1 com títulos

  // mede altura real de uma linha usando dois retângulos consecutivos
  r0   := AGrid.CellRect(0, LLinhaBase);
  r1   := AGrid.CellRect(0, LLinhaBase + 1);
  rowH := r1.Top - r0.Top;

  // fallback se ainda não deu para medir (grid vazio/não pintado)
  if rowH <= 0 then
    rowH := AGrid.DefaultRowHeight + 1; // não usa GridLineWidth (protected)

  // topo da área de dados
  if r0.Top > 0 then
    topData := r0.Top
  else if LTemTitulo then
    topData := AGrid.DefaultRowHeight + 1
  else
    topData := 0;

  visH := AGrid.ClientHeight - topData;

  if (rowH <= 0) or (visH <= 0) then
    Exit(0);

  Result := Max(0, visH div rowH);
end;
