@@ -194,9 +194,14 @@ class CustomImage {
194194 * Resize the image to the given dimensions. This method uses the canvas API to perform the resizing.
195195 * @param {number } width - The width of the new image.
196196 * @param {number } height - The height of the new image.
197+ * @param {object } options - Additional options for resizing.
198+ * @param {string } [options.resample] - The resampling method to use. Can be one of `nearest`, `bilinear`, `bicubic`.
197199 * @returns {Promise<CustomImage> } - `this` to support chaining.
198200 */
199- async resize ( width , height ) {
201+ async resize ( width , height , {
202+ // TODO: Use `resample`
203+ resample = 'bilinear' ,
204+ } = { } ) {
200205 if ( CanvasClass ) {
201206 // Store number of channels before resizing
202207 let numChannels = this . channels ;
@@ -235,6 +240,127 @@ class CustomImage {
235240
236241 }
237242
243+ async center_crop ( crop_width , crop_height ) {
244+ // If the image is already the desired size, return it
245+ if ( this . width === crop_width && this . height === crop_height ) {
246+ return this ;
247+ }
248+
249+ // Determine bounds of the image in the new canvas
250+ let width_offset = ( this . width - crop_width ) / 2 ;
251+ let height_offset = ( this . height - crop_height ) / 2 ;
252+
253+
254+ if ( CanvasClass ) {
255+ // Store number of channels before resizing
256+ let numChannels = this . channels ;
257+
258+ // Create canvas object for this image
259+ let canvas = this . toCanvas ( ) ;
260+
261+ // Create a new canvas of the desired size. This is needed since if the
262+ // image is too small, we need to pad it with black pixels.
263+ const ctx = new CanvasClass ( crop_width , crop_height ) . getContext ( '2d' ) ;
264+
265+ let sourceX = 0 ;
266+ let sourceY = 0 ;
267+ let destX = 0 ;
268+ let destY = 0 ;
269+
270+ if ( width_offset >= 0 ) {
271+ sourceX = width_offset ;
272+ } else {
273+ destX = - width_offset ;
274+ }
275+
276+ if ( height_offset >= 0 ) {
277+ sourceY = height_offset ;
278+ } else {
279+ destY = - height_offset ;
280+ }
281+
282+ // Draw image to context, cropping in the process
283+ ctx . drawImage ( canvas ,
284+ sourceX , sourceY , crop_width , crop_height ,
285+ destX , destY , crop_width , crop_height
286+ ) ;
287+
288+ // Create image from the resized data
289+ let resizedImage = new CustomImage ( ctx . getImageData ( 0 , 0 , crop_width , crop_height ) . data , crop_width , crop_height , 4 ) ;
290+
291+ // Convert back so that image has the same number of channels as before
292+ return resizedImage . convert ( numChannels ) ;
293+
294+ } else {
295+ // Create sharp image from raw data
296+ let img = sharp ( this . data , {
297+ raw : {
298+ width : this . width ,
299+ height : this . height ,
300+ channels : this . channels
301+ }
302+ } ) ;
303+
304+ if ( width_offset >= 0 && height_offset >= 0 ) {
305+ // Cropped image lies entirely within the original image
306+ img = img . extract ( {
307+ left : Math . floor ( width_offset ) ,
308+ top : Math . floor ( height_offset ) ,
309+ width : crop_width ,
310+ height : crop_height ,
311+ } )
312+ } else if ( width_offset <= 0 && height_offset <= 0 ) {
313+ // Cropped image lies entirely outside the original image,
314+ // so we add padding
315+ let top = Math . floor ( - height_offset ) ;
316+ let left = Math . floor ( - width_offset ) ;
317+ img = img . extend ( {
318+ top : top ,
319+ left : left ,
320+
321+ // Ensures the resulting image has the desired dimensions
322+ right : crop_width - this . width - left ,
323+ bottom : crop_height - this . height - top ,
324+ } ) ;
325+ } else {
326+ // Cropped image lies partially outside the original image.
327+ // We first pad, then crop.
328+
329+ let y_padding = [ 0 , 0 ] ;
330+ let y_extract = 0 ;
331+ if ( height_offset < 0 ) {
332+ y_padding [ 0 ] = Math . floor ( - height_offset ) ;
333+ y_padding [ 1 ] = crop_height - this . height - y_padding [ 0 ] ;
334+ } else {
335+ y_extract = Math . floor ( height_offset ) ;
336+ }
337+
338+ let x_padding = [ 0 , 0 ] ;
339+ let x_extract = 0 ;
340+ if ( width_offset < 0 ) {
341+ x_padding [ 0 ] = Math . floor ( - width_offset ) ;
342+ x_padding [ 1 ] = crop_width - this . width - x_padding [ 0 ] ;
343+ } else {
344+ x_extract = Math . floor ( width_offset ) ;
345+ }
346+
347+ img = img . extend ( {
348+ top : y_padding [ 0 ] ,
349+ bottom : y_padding [ 1 ] ,
350+ left : x_padding [ 0 ] ,
351+ right : x_padding [ 1 ] ,
352+ } ) . extract ( {
353+ left : x_extract ,
354+ top : y_extract ,
355+ width : crop_width ,
356+ height : crop_height ,
357+ } )
358+ }
359+
360+ return await loadImageFunction ( img ) ;
361+ }
362+ }
363+
238364 toCanvas ( ) {
239365 // Clone, and convert data to RGBA before drawing to canvas.
240366 // This is because the canvas API only supports RGBA
0 commit comments