@@ -1055,14 +1055,51 @@ def make_image(self, renderer, magnification=1.0, unsampled=False):
1055
1055
self ._is_grayscale = False
1056
1056
vl = self .axes .viewLim
1057
1057
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.
1066
1103
return im , l , b , IdentityTransform ()
1067
1104
1068
1105
def set_data (self , x , y , A ):
@@ -1186,27 +1223,33 @@ def make_image(self, renderer, magnification=1.0, unsampled=False):
1186
1223
raise RuntimeError ('You must first set the image array' )
1187
1224
if unsampled :
1188
1225
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
+
1192
1238
l , b , r , t = self .axes .bbox .extents
1193
1239
width = (round (r ) + 0.5 ) - (round (l ) - 0.5 )
1194
1240
height = (round (t ) + 0.5 ) - (round (b ) - 0.5 )
1195
1241
width = int (round (width * magnification ))
1196
1242
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
1204
1243
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 )))
1210
1253
return im , l , b , IdentityTransform ()
1211
1254
1212
1255
def _check_unsampled_image (self ):
0 commit comments