There have been various proposals to do that. Assuming that we want to keep our fingers/paws away from transform canvas, one of the most directly applicable answer seems to be Paul Gaborit's solution.
However, I take the question as challenge to hack to path to yield the desired result. To this end, let us first recall why the above code does not allow fills. You decompose the contour into disconnected little segments, which cannot be filled. If you did not, the to path hack wouldn't work. So what can one do? One ad hoc possibility is to employ execute at end to. The downside is that the hack draws the polygon more than once, and in the earlier incarnations only partially (which one could fix if the user is ready to supply the number of corners), on the bright side it turns the collection of disconnected paths into a polygon with nice line joins. The fill style etc. is stored in fancy reflect/.style.
\documentclass{report}
\usepackage{tikz}
\usetikzlibrary{calc}
\newcounter{reflectedcoord}
\tikzset{fancy reflect at/.style args={#1--#2}{ini reflect,to path={%
($2*($(#1)!(\tikztostart)!(#2)$)-(\tikztostart)$)
\pgfextra{\stepcounter{reflectedcoord}}%
coordinate(reflectedcoord-\number\value{reflectedcoord})
-- ($2*($(#1)!(\tikztotarget)!(#2)$)-(\tikztotarget)$)
},execute at end to={%\typeout{\number\value{reflectedcoord}}
\ifnum\value{reflectedcoord}>1
\path[fancy reflect,polygon through={reflectedcoord-1,reflectedcoord-...,reflectedcoord-\number\value{reflectedcoord}}];
\fi}},ini reflect/.code={\setcounter{reflectedcoord}{0}},
polygon through/.style={insert path={
plot[samples at={#1}] (\x) -- cycle}},
fancy reflect/.style={fill=red,draw,thick}}
\begin{document}
\begin{tikzpicture}[scale=1]
\draw [very thin, gray] (2,1) grid (12,9);
\node (X) at (7,2) {};
\node (Y) at (7,8) {};
\draw[color=red,very thick](X.center)--(Y.center);
\begin{scope}
\foreach \point [count=\i] in {{(4,2)},{(3,2)},{(3,4)},{(4,4)}} {
\node (A\i) at \point {};
}
\draw[fill=black] (A1.center)--(A2.center)--(A3.center)--(A4.center)--cycle;
\draw[fancy reflect at=X--Y] (A1) to (A2) (A2) to (A3) (A3) to (A4) (A4) to (A1);
\end{scope}
\begin{scope}
\foreach \point [count=\i] in {{(3,6)},{(4,6)},{(4,8)},{(3,8)}} {
\node (A\i) at \point {};
}
\draw[fill=black] (A1.center)--(A2.center)--(A3.center)--(A4.center)--cycle;
\draw[fancy reflect at=X--Y] (A1) to (A2) (A2) to (A3) (A3) to (A4) (A4) to (A1);
\end{scope}
\begin{scope}
\foreach \point [count=\i] in {{(5,5)},{(5,7)},{(7,5)},{(7,3)}} {
\node (A\i) at \point {};
}
\draw[fill=black] (A1.center)--(A2.center)--(A3.center)--(A4.center)--cycle;
\draw[fancy reflect at=X--Y] (A1) to (A2) (A2) to (A3) (A3) to (A4) (A4) to (A1);
\end{scope}
\begin{scope}
\foreach \point [count=\i] in {{(9,5)},{(9,7)},{(10,8)},{(10,6)}} {
\node (A\i) at \point {};
}
\draw[fill=black] (A1.center)--(A2.center)--(A3.center)--(A4.center)--cycle;
\draw[fancy reflect at=X--Y] (A1) to (A2) (A2) to (A3) (A3) to (A4) (A4) to (A1);
\end{scope}
\begin{scope}
\foreach \point [count=\i] in {{(10,4)},{(10,6)},{(11,6)},{(11,4)}} {
\node (A\i) at \point {};
}
\draw[fill=black] (A1.center)--(A2.center)--(A3.center)--(A4.center)--cycle;
\draw[fancy reflect at=X--Y] (A1) to (A2) (A2) to (A3) (A3) to (A4) (A4) to (A1);
\end{scope}
\end{tikzpicture}
\end{document}

As one can see, the line joins of the intermediate polygons cause some dismay (which one could turn off with line join=round, say). The cleaner solution is to let the user supply the number of corners because this also works with nontrivial opacities and so on.
\documentclass{report}
\usepackage{tikz}
\usetikzlibrary{calc}
\newcounter{reflectedcoord}
\tikzset{fancy reflect at/.style args={#1--#2}{ini reflect,to path={%
($2*($(#1)!(\tikztostart)!(#2)$)-(\tikztostart)$)
\pgfextra{\stepcounter{reflectedcoord}}%
coordinate(reflectedcoord-\number\value{reflectedcoord})
-- ($2*($(#1)!(\tikztotarget)!(#2)$)-(\tikztotarget)$)
},execute at end to={%\typeout{\number\value{reflectedcoord}}
\ifnum\value{reflectedcoord}=\pgfkeysvalueof{/tikz/n reflect}
\path[fancy reflect,polygon through={reflectedcoord-1,reflectedcoord-...,reflectedcoord-\number\value{reflectedcoord}}];
\fi}},ini reflect/.code={\setcounter{reflectedcoord}{0}},
polygon through/.style={insert path={
plot[samples at={#1}] (\x) -- cycle}},
fancy reflect/.style={fill=red,draw,thick},
n reflect/.initial=4}
\begin{document}
\begin{tikzpicture}[scale=1]
\draw [very thin, gray] (2,1) grid (12,9);
\node (X) at (7,2) {};
\node (Y) at (7,8) {};
\draw[color=red,very thick](X.center)--(Y.center);
\begin{scope}
\foreach \point [count=\i] in {{(4,2)},{(3,2)},{(3,4)},{(4,4)}} {
\node (A\i) at \point {};
}
\draw[fill=black] (A1.center)--(A2.center)--(A3.center)--(A4.center)--cycle;
\draw[fancy reflect at=X--Y] (A1) to (A2) (A2) to (A3) (A3) to (A4) (A4) to (A1);
\end{scope}
\begin{scope}
\foreach \point [count=\i] in {{(3,6)},{(4,6)},{(4,8)},{(3,8)}} {
\node (A\i) at \point {};
}
\draw[fill=black] (A1.center)--(A2.center)--(A3.center)--(A4.center)--cycle;
\path[fancy reflect at=X--Y] (A1) to (A2) (A2) to (A3) (A3) to (A4) (A4) to (A1);
\end{scope}
\begin{scope}
\foreach \point [count=\i] in {{(5,5)},{(5,7)},{(7,5)},{(7,3)}} {
\node (A\i) at \point {};
}
\draw[fill=black] (A1.center)--(A2.center)--(A3.center)--(A4.center)--cycle;
\path[fancy reflect at=X--Y] (A1) to (A2) (A2) to (A3) (A3) to (A4) (A4) to (A1);
\end{scope}
\begin{scope}
\foreach \point [count=\i] in {{(9,5)},{(9,7)},{(10,8)},{(10,6)}} {
\node (A\i) at \point {};
}
\draw[fill=black] (A1.center)--(A2.center)--(A3.center)--(A4.center)--cycle;
\path[fancy reflect at=X--Y] (A1) to (A2) (A2) to (A3) (A3) to (A4) (A4) to (A1);
\end{scope}
\begin{scope}
\foreach \point [count=\i] in {{(10,4)},{(10,6)},{(11,6)},{(11,4)}} {
\node (A\i) at \point {};
}
\draw[fill=black] (A1.center)--(A2.center)--(A3.center)--(A4.center)--cycle;
\path[fancy reflect at=X--Y] (A1) to (A2) (A2) to (A3) (A3) to (A4) (A4) to (A1);
\end{scope}
\end{tikzpicture}
\end{document}

to path. You could use Paul Gaborit's solution instead. – May 01 '20 at 15:10