@@ -1052,14 +1052,49 @@ def make_image(self, renderer, magnification=1.0, unsampled=False):
1052
1052
self ._is_grayscale = False
1053
1053
vl = self .axes .viewLim
1054
1054
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
+ (x_pix - self ._Ax [x_int ]) / np .diff (self ._Ax )[x_int ], 0 , 1 ,
1080
+ dtype = np .float32 ) # Downcasting helps with speed.
1081
+ y_frac = np .clip (
1082
+ (y_pix - self ._Ay [y_int ]) / np .diff (self ._Ay )[y_int ], 0 , 1 ,
1083
+ dtype = np .float32 )
1084
+ f00 = np .outer (1 - y_frac , 1 - x_frac )
1085
+ f10 = np .outer (y_frac , 1 - x_frac )
1086
+ f01 = np .outer (1 - y_frac , x_frac )
1087
+ f11 = np .outer (y_frac , x_frac )
1088
+ im = np .empty ((height , width , 4 ), np .uint8 )
1089
+ for chan in range (4 ):
1090
+ ac = A [:, :, chan ].reshape (- 1 ) # reshape(-1) avoids a copy.
1091
+ # Shifting the buffer start (`ac[offset:]`) avoids an array
1092
+ # addition (`ac[idx_int + offset]`).
1093
+ buf = f00 * ac [idx_int ]
1094
+ buf += f10 * ac [A .shape [1 ]:][idx_int ]
1095
+ buf += f01 * ac [1 :][idx_int ]
1096
+ buf += f11 * ac [A .shape [1 ] + 1 :][idx_int ]
1097
+ im [:, :, chan ] = buf # Implicitly casts to uint8.
1063
1098
return im , l , b , IdentityTransform ()
1064
1099
1065
1100
def set_data (self , x , y , A ):
@@ -1183,27 +1218,35 @@ def make_image(self, renderer, magnification=1.0, unsampled=False):
1183
1218
raise RuntimeError ('You must first set the image array' )
1184
1219
if unsampled :
1185
1220
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 ))
1221
+
1194
1222
if self ._rgbacache is None :
1195
1223
A = self .to_rgba (self ._A , bytes = True )
1196
- self ._rgbacache = A
1224
+ padded_A = np .pad (A , [(1 , 1 ), (1 , 1 ), (0 , 0 )])
1225
+ self ._rgbacache = padded_A
1197
1226
if self ._A .ndim == 2 :
1198
1227
self ._is_grayscale = self .cmap .is_gray ()
1199
1228
else :
1200
- A = self ._rgbacache
1229
+ padded_A = self ._rgbacache
1230
+ bg = mcolors .to_rgba (self .axes .patch .get_facecolor (), 0 )
1231
+ bg = (np .array (bg ) * 255 ).astype (np .uint8 )
1232
+ if (padded_A [0 , 0 ] != bg ).all ():
1233
+ padded_A [[0 , - 1 ], :] = padded_A [:, [0 , - 1 ]] = bg
1234
+
1235
+ l , b , r , t = self .axes .bbox .extents
1236
+ width = (round (r ) + 0.5 ) - (round (l ) - 0.5 )
1237
+ height = (round (t ) + 0.5 ) - (round (b ) - 0.5 )
1238
+ width = int (round (width * magnification ))
1239
+ height = int (round (height * magnification ))
1201
1240
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 )
1241
+
1242
+ x_pix = np .linspace (vl .x0 , vl .x1 , width )
1243
+ y_pix = np .linspace (vl .y0 , vl .y1 , height )
1244
+ x_int = self ._Ax .searchsorted (x_pix )
1245
+ y_int = self ._Ay .searchsorted (y_pix )
1246
+ im = ( # See comment in NonUniformImage.make_image re: performance.
1247
+ padded_A .view (np .uint32 ).ravel ()[
1248
+ np .add .outer (y_int * padded_A .shape [1 ], x_int )]
1249
+ .view (np .uint8 ).reshape ((height , width , 4 )))
1207
1250
return im , l , b , IdentityTransform ()
1208
1251
1209
1252
def _check_unsampled_image (self ):
0 commit comments