@@ -1055,14 +1055,51 @@ def make_image(self, renderer, magnification=1.0, unsampled=False):
10551055 self ._is_grayscale = False
10561056 vl = self .axes .viewLim
10571057 l , b , r , t = self .axes .bbox .extents
1058- width = (round (r ) + 0.5 ) - (round (l ) - 0.5 )
1059- height = (round (t ) + 0.5 ) - (round (b ) - 0.5 )
1060- width *= magnification
1061- height *= magnification
1062- im = _image .pcolor (self ._Ax , self ._Ay , A ,
1063- int (height ), int (width ),
1064- (vl .x0 , vl .x1 , vl .y0 , vl .y1 ),
1065- _interpd_ [self ._interpolation ])
1058+ width = int (((round (r ) + 0.5 ) - (round (l ) - 0.5 )) * magnification )
1059+ height = int (((round (t ) + 0.5 ) - (round (b ) - 0.5 )) * magnification )
1060+ x_pix = np .linspace (vl .x0 , vl .x1 , width )
1061+ y_pix = np .linspace (vl .y0 , vl .y1 , height )
1062+ if self ._interpolation == "nearest" :
1063+ x_mid = (self ._Ax [:- 1 ] + self ._Ax [1 :]) / 2
1064+ y_mid = (self ._Ay [:- 1 ] + self ._Ay [1 :]) / 2
1065+ x_int = x_mid .searchsorted (x_pix )
1066+ y_int = y_mid .searchsorted (y_pix )
1067+ # The following is equal to `A[y_int[:, None], x_int[None, :]]`,
1068+ # but many times faster. Both casting to uint32 (to have an
1069+ # effectively 1D array) and manual index flattening matter.
1070+ im = (
1071+ np .ascontiguousarray (A ).view (np .uint32 ).ravel ()[
1072+ np .add .outer (y_int * A .shape [1 ], x_int )]
1073+ .view (np .uint8 ).reshape ((height , width , 4 )))
1074+ else : # self._interpolation == "bilinear"
1075+ # Use np.interp to compute x_int/x_float has similar speed.
1076+ x_int = np .clip (
1077+ self ._Ax .searchsorted (x_pix ) - 1 , 0 , len (self ._Ax ) - 2 )
1078+ y_int = np .clip (
1079+ self ._Ay .searchsorted (y_pix ) - 1 , 0 , len (self ._Ay ) - 2 )
1080+ idx_int = np .add .outer (y_int * A .shape [1 ], x_int )
1081+ x_frac = np .clip (
1082+ np .divide (x_pix - self ._Ax [x_int ], np .diff (self ._Ax )[x_int ],
1083+ dtype = np .float32 ), # Downcasting helps with speed.
1084+ 0 , 1 )
1085+ y_frac = np .clip (
1086+ np .divide (y_pix - self ._Ay [y_int ], np .diff (self ._Ay )[y_int ],
1087+ dtype = np .float32 ),
1088+ 0 , 1 )
1089+ f00 = np .outer (1 - y_frac , 1 - x_frac )
1090+ f10 = np .outer (y_frac , 1 - x_frac )
1091+ f01 = np .outer (1 - y_frac , x_frac )
1092+ f11 = np .outer (y_frac , x_frac )
1093+ im = np .empty ((height , width , 4 ), np .uint8 )
1094+ for chan in range (4 ):
1095+ ac = A [:, :, chan ].reshape (- 1 ) # reshape(-1) avoids a copy.
1096+ # Shifting the buffer start (`ac[offset:]`) avoids an array
1097+ # addition (`ac[idx_int + offset]`).
1098+ buf = f00 * ac [idx_int ]
1099+ buf += f10 * ac [A .shape [1 ]:][idx_int ]
1100+ buf += f01 * ac [1 :][idx_int ]
1101+ buf += f11 * ac [A .shape [1 ] + 1 :][idx_int ]
1102+ im [:, :, chan ] = buf # Implicitly casts to uint8.
10661103 return im , l , b , IdentityTransform ()
10671104
10681105 def set_data (self , x , y , A ):
@@ -1186,27 +1223,33 @@ def make_image(self, renderer, magnification=1.0, unsampled=False):
11861223 raise RuntimeError ('You must first set the image array' )
11871224 if unsampled :
11881225 raise ValueError ('unsampled not supported on PColorImage' )
1189- fc = self .axes .patch .get_facecolor ()
1190- bg = mcolors .to_rgba (fc , 0 )
1191- bg = (np .array (bg )* 255 ).astype (np .uint8 )
1226+
1227+ if self ._rgbacache is None :
1228+ A = self .to_rgba (self ._A , bytes = True )
1229+ self ._rgbacache = np .pad (A , [(1 , 1 ), (1 , 1 ), (0 , 0 )], "constant" )
1230+ if self ._A .ndim == 2 :
1231+ self ._is_grayscale = self .cmap .is_gray ()
1232+ padded_A = self ._rgbacache
1233+ bg = mcolors .to_rgba (self .axes .patch .get_facecolor (), 0 )
1234+ bg = (np .array (bg ) * 255 ).astype (np .uint8 )
1235+ if (padded_A [0 , 0 ] != bg ).all ():
1236+ padded_A [[0 , - 1 ], :] = padded_A [:, [0 , - 1 ]] = bg
1237+
11921238 l , b , r , t = self .axes .bbox .extents
11931239 width = (round (r ) + 0.5 ) - (round (l ) - 0.5 )
11941240 height = (round (t ) + 0.5 ) - (round (b ) - 0.5 )
11951241 width = int (round (width * magnification ))
11961242 height = int (round (height * magnification ))
1197- if self ._rgbacache is None :
1198- A = self .to_rgba (self ._A , bytes = True )
1199- self ._rgbacache = A
1200- if self ._A .ndim == 2 :
1201- self ._is_grayscale = self .cmap .is_gray ()
1202- else :
1203- A = self ._rgbacache
12041243 vl = self .axes .viewLim
1205- im = _image .pcolor2 (self ._Ax , self ._Ay , A ,
1206- height ,
1207- width ,
1208- (vl .x0 , vl .x1 , vl .y0 , vl .y1 ),
1209- bg )
1244+
1245+ x_pix = np .linspace (vl .x0 , vl .x1 , width )
1246+ y_pix = np .linspace (vl .y0 , vl .y1 , height )
1247+ x_int = self ._Ax .searchsorted (x_pix )
1248+ y_int = self ._Ay .searchsorted (y_pix )
1249+ im = ( # See comment in NonUniformImage.make_image re: performance.
1250+ padded_A .view (np .uint32 ).ravel ()[
1251+ np .add .outer (y_int * padded_A .shape [1 ], x_int )]
1252+ .view (np .uint8 ).reshape ((height , width , 4 )))
12101253 return im , l , b , IdentityTransform ()
12111254
12121255 def _check_unsampled_image (self ):
0 commit comments