@@ -1052,14 +1052,51 @@ 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
+ 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.
1063
1100
return im , l , b , IdentityTransform ()
1064
1101
1065
1102
def set_data (self , x , y , A ):
@@ -1183,27 +1220,35 @@ def make_image(self, renderer, magnification=1.0, unsampled=False):
1183
1220
raise RuntimeError ('You must first set the image array' )
1184
1221
if unsampled :
1185
1222
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
+
1194
1224
if self ._rgbacache is None :
1195
1225
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
1197
1228
if self ._A .ndim == 2 :
1198
1229
self ._is_grayscale = self .cmap .is_gray ()
1199
1230
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 ))
1201
1242
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 )))
1207
1252
return im , l , b , IdentityTransform ()
1208
1253
1209
1254
def _check_unsampled_image (self ):
0 commit comments