@@ -1052,14 +1052,51 @@ def make_image(self, renderer, magnification=1.0, unsampled=False):
10521052 self ._is_grayscale = False
10531053 vl = self .axes .viewLim
10541054 l , b , r , t = self .axes .bbox .extents
1055- width = (round (r ) + 0.5 ) - (round (l ) - 0.5 )
1056- height = (round (t ) + 0.5 ) - (round (b ) - 0.5 )
1057- width *= magnification
1058- height *= magnification
1059- im = _image .pcolor (self ._Ax , self ._Ay , A ,
1060- int (height ), int (width ),
1061- (vl .x0 , vl .x1 , vl .y0 , vl .y1 ),
1062- _interpd_ [self ._interpolation ])
1055+ width = int (((round (r ) + 0.5 ) - (round (l ) - 0.5 )) * magnification )
1056+ height = int (((round (t ) + 0.5 ) - (round (b ) - 0.5 )) * magnification )
1057+ x_pix = np .linspace (vl .x0 , vl .x1 , width )
1058+ y_pix = np .linspace (vl .y0 , vl .y1 , height )
1059+ if self ._interpolation == "nearest" :
1060+ x_mid = (self ._Ax [:- 1 ] + self ._Ax [1 :]) / 2
1061+ y_mid = (self ._Ay [:- 1 ] + self ._Ay [1 :]) / 2
1062+ x_int = x_mid .searchsorted (x_pix )
1063+ y_int = y_mid .searchsorted (y_pix )
1064+ # The following is equal to `A[y_int[:, None], x_int[None, :]]`,
1065+ # but many times faster. Both casting to uint32 (to have an
1066+ # effectively 1D array) and manual index flattening matter.
1067+ im = (
1068+ np .ascontiguousarray (A ).view (np .uint32 ).ravel ()[
1069+ np .add .outer (y_int * A .shape [1 ], x_int )]
1070+ .view (np .uint8 ).reshape ((height , width , 4 )))
1071+ else : # self._interpolation == "bilinear"
1072+ # Use np.interp to compute x_int/x_float has similar speed.
1073+ x_int = np .clip (
1074+ self ._Ax .searchsorted (x_pix ) - 1 , 0 , len (self ._Ax ) - 2 )
1075+ y_int = np .clip (
1076+ self ._Ay .searchsorted (y_pix ) - 1 , 0 , len (self ._Ay ) - 2 )
1077+ idx_int = np .add .outer (y_int * A .shape [1 ], x_int )
1078+ x_frac = np .clip (
1079+ np .divide (x_pix - self ._Ax [x_int ], np .diff (self ._Ax )[x_int ],
1080+ dtype = np .float32 ), # Downcasting helps with speed.
1081+ 0 , 1 )
1082+ y_frac = np .clip (
1083+ np .divide (y_pix - self ._Ay [y_int ], np .diff (self ._Ay )[y_int ],
1084+ dtype = np .float32 ),
1085+ 0 , 1 )
1086+ f00 = np .outer (1 - y_frac , 1 - x_frac )
1087+ f10 = np .outer (y_frac , 1 - x_frac )
1088+ f01 = np .outer (1 - y_frac , x_frac )
1089+ f11 = np .outer (y_frac , x_frac )
1090+ im = np .empty ((height , width , 4 ), np .uint8 )
1091+ for chan in range (4 ):
1092+ ac = A [:, :, chan ].reshape (- 1 ) # reshape(-1) avoids a copy.
1093+ # Shifting the buffer start (`ac[offset:]`) avoids an array
1094+ # addition (`ac[idx_int + offset]`).
1095+ buf = f00 * ac [idx_int ]
1096+ buf += f10 * ac [A .shape [1 ]:][idx_int ]
1097+ buf += f01 * ac [1 :][idx_int ]
1098+ buf += f11 * ac [A .shape [1 ] + 1 :][idx_int ]
1099+ im [:, :, chan ] = buf # Implicitly casts to uint8.
10631100 return im , l , b , IdentityTransform ()
10641101
10651102 def set_data (self , x , y , A ):
@@ -1183,27 +1220,35 @@ def make_image(self, renderer, magnification=1.0, unsampled=False):
11831220 raise RuntimeError ('You must first set the image array' )
11841221 if unsampled :
11851222 raise ValueError ('unsampled not supported on PColorImage' )
1186- fc = self .axes .patch .get_facecolor ()
1187- bg = mcolors .to_rgba (fc , 0 )
1188- bg = (np .array (bg )* 255 ).astype (np .uint8 )
1189- l , b , r , t = self .axes .bbox .extents
1190- width = (round (r ) + 0.5 ) - (round (l ) - 0.5 )
1191- height = (round (t ) + 0.5 ) - (round (b ) - 0.5 )
1192- width = int (round (width * magnification ))
1193- height = int (round (height * magnification ))
1223+
11941224 if self ._rgbacache is None :
11951225 A = self .to_rgba (self ._A , bytes = True )
1196- self ._rgbacache = A
1226+ padded_A = np .pad (A , [(1 , 1 ), (1 , 1 ), (0 , 0 )])
1227+ self ._rgbacache = padded_A
11971228 if self ._A .ndim == 2 :
11981229 self ._is_grayscale = self .cmap .is_gray ()
11991230 else :
1200- A = self ._rgbacache
1231+ padded_A = self ._rgbacache
1232+ bg = mcolors .to_rgba (self .axes .patch .get_facecolor (), 0 )
1233+ bg = (np .array (bg ) * 255 ).astype (np .uint8 )
1234+ if (padded_A [0 , 0 ] != bg ).all ():
1235+ padded_A [[0 , - 1 ], :] = padded_A [:, [0 , - 1 ]] = bg
1236+
1237+ l , b , r , t = self .axes .bbox .extents
1238+ width = (round (r ) + 0.5 ) - (round (l ) - 0.5 )
1239+ height = (round (t ) + 0.5 ) - (round (b ) - 0.5 )
1240+ width = int (round (width * magnification ))
1241+ height = int (round (height * magnification ))
12011242 vl = self .axes .viewLim
1202- im = _image .pcolor2 (self ._Ax , self ._Ay , A ,
1203- height ,
1204- width ,
1205- (vl .x0 , vl .x1 , vl .y0 , vl .y1 ),
1206- bg )
1243+
1244+ x_pix = np .linspace (vl .x0 , vl .x1 , width )
1245+ y_pix = np .linspace (vl .y0 , vl .y1 , height )
1246+ x_int = self ._Ax .searchsorted (x_pix )
1247+ y_int = self ._Ay .searchsorted (y_pix )
1248+ im = ( # See comment in NonUniformImage.make_image re: performance.
1249+ padded_A .view (np .uint32 ).ravel ()[
1250+ np .add .outer (y_int * padded_A .shape [1 ], x_int )]
1251+ .view (np .uint8 ).reshape ((height , width , 4 )))
12071252 return im , l , b , IdentityTransform ()
12081253
12091254 def _check_unsampled_image (self ):
0 commit comments