mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 01:02:25 -05:00 
			
		
		
		
	[chore] Only call imaging.Resize when necessary, use even tinier blurhashes (#3247)
		
	* [chore] Use `imaging.Fit`, use even tinier blurhashes * avoid calling resize if not necessary * update blurhashes + thumb
This commit is contained in:
		
					parent
					
						
							
								277b043633
							
						
					
				
			
			
				commit
				
					
						e10aa76612
					
				
			
		
					 7 changed files with 60 additions and 53 deletions
				
			
		|  | @ -858,7 +858,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch8() { | ||||||
|   "static_url": "http://localhost:8080/fileserver/01AY6P665V14JJR0AFVRT7311Y/attachment/small/`+instanceAccount.AvatarMediaAttachment.ID+`.webp",`+` |   "static_url": "http://localhost:8080/fileserver/01AY6P665V14JJR0AFVRT7311Y/attachment/small/`+instanceAccount.AvatarMediaAttachment.ID+`.webp",`+` | ||||||
|   "thumbnail_static_type": "image/webp", |   "thumbnail_static_type": "image/webp", | ||||||
|   "thumbnail_description": "A bouncing little green peglin.", |   "thumbnail_description": "A bouncing little green peglin.", | ||||||
|   "blurhash": "LE9as6M}4YtO%dRlWEt6Dmoxx?WC" |   "blurhash": "LF9kG$RR4YtP%dR+V^t5D,oxx?WC" | ||||||
| }`, string(instanceV2ThumbnailJson)) | }`, string(instanceV2ThumbnailJson)) | ||||||
| 
 | 
 | ||||||
| 	// double extra special bonus: now update the image description without changing the image | 	// double extra special bonus: now update the image description without changing the image | ||||||
|  |  | ||||||
|  | @ -206,7 +206,7 @@ func (suite *MediaCreateTestSuite) TestMediaCreateSuccessful() { | ||||||
| 			Y: 0.5, | 			Y: 0.5, | ||||||
| 		}, | 		}, | ||||||
| 	}, *attachmentReply.Meta) | 	}, *attachmentReply.Meta) | ||||||
| 	suite.Equal("LjCGfG#6RkRn_NvzRjWF?urqV@a$", *attachmentReply.Blurhash) | 	suite.Equal("LiBzRk#6V[WF_NvzV@WY_3rqV@a$", *attachmentReply.Blurhash) | ||||||
| 	suite.NotEmpty(attachmentReply.ID) | 	suite.NotEmpty(attachmentReply.ID) | ||||||
| 	suite.NotEmpty(attachmentReply.URL) | 	suite.NotEmpty(attachmentReply.URL) | ||||||
| 	suite.NotEmpty(attachmentReply.PreviewURL) | 	suite.NotEmpty(attachmentReply.PreviewURL) | ||||||
|  | @ -291,7 +291,7 @@ func (suite *MediaCreateTestSuite) TestMediaCreateSuccessfulV2() { | ||||||
| 			Y: 0.5, | 			Y: 0.5, | ||||||
| 		}, | 		}, | ||||||
| 	}, *attachmentReply.Meta) | 	}, *attachmentReply.Meta) | ||||||
| 	suite.Equal("LjCGfG#6RkRn_NvzRjWF?urqV@a$", *attachmentReply.Blurhash) | 	suite.Equal("LiBzRk#6V[WF_NvzV@WY_3rqV@a$", *attachmentReply.Blurhash) | ||||||
| 	suite.NotEmpty(attachmentReply.ID) | 	suite.NotEmpty(attachmentReply.ID) | ||||||
| 	suite.Nil(attachmentReply.URL) | 	suite.Nil(attachmentReply.URL) | ||||||
| 	suite.NotEmpty(attachmentReply.PreviewURL) | 	suite.NotEmpty(attachmentReply.PreviewURL) | ||||||
|  |  | ||||||
|  | @ -276,7 +276,7 @@ func (suite *ManagerTestSuite) TestSimpleJpegProcess() { | ||||||
| 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType) | 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType) | ||||||
| 	suite.Equal(269739, attachment.File.FileSize) | 	suite.Equal(269739, attachment.File.FileSize) | ||||||
| 	suite.Equal(22858, attachment.Thumbnail.FileSize) | 	suite.Equal(22858, attachment.Thumbnail.FileSize) | ||||||
| 	suite.Equal("LjCGfG#6RkRn_NvzRjWF?urqV@a$", attachment.Blurhash) | 	suite.Equal("LiBzRk#6V[WF_NvzV@WY_3rqV@a$", attachment.Blurhash) | ||||||
| 
 | 
 | ||||||
| 	// now make sure the attachment is in the database | 	// now make sure the attachment is in the database | ||||||
| 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | ||||||
|  | @ -429,7 +429,7 @@ func (suite *ManagerTestSuite) TestSlothVineProcess() { | ||||||
| 	suite.Equal("image/webp", attachment.Thumbnail.ContentType) | 	suite.Equal("image/webp", attachment.Thumbnail.ContentType) | ||||||
| 	suite.Equal(312453, attachment.File.FileSize) | 	suite.Equal(312453, attachment.File.FileSize) | ||||||
| 	suite.Equal(5648, attachment.Thumbnail.FileSize) | 	suite.Equal(5648, attachment.Thumbnail.FileSize) | ||||||
| 	suite.Equal("LhIrNMt6Nsj[t7ayW.j[_4WBsWkB", attachment.Blurhash) | 	suite.Equal("LfIYH~xtNskCxtfPW.kB_4aespof", attachment.Blurhash) | ||||||
| 
 | 
 | ||||||
| 	// now make sure the attachment is in the database | 	// now make sure the attachment is in the database | ||||||
| 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | ||||||
|  | @ -489,7 +489,7 @@ func (suite *ManagerTestSuite) TestLongerMp4Process() { | ||||||
| 	suite.Equal("image/webp", attachment.Thumbnail.ContentType) | 	suite.Equal("image/webp", attachment.Thumbnail.ContentType) | ||||||
| 	suite.Equal(109569, attachment.File.FileSize) | 	suite.Equal(109569, attachment.File.FileSize) | ||||||
| 	suite.Equal(2976, attachment.Thumbnail.FileSize) | 	suite.Equal(2976, attachment.Thumbnail.FileSize) | ||||||
| 	suite.Equal("L8QJfm~qD%_3_3D%t7RjM{j[ofRj", attachment.Blurhash) | 	suite.Equal("LJQJfm?bM{?b~qRjt7WBayWBofWB", attachment.Blurhash) | ||||||
| 
 | 
 | ||||||
| 	// now make sure the attachment is in the database | 	// now make sure the attachment is in the database | ||||||
| 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | ||||||
|  | @ -549,7 +549,7 @@ func (suite *ManagerTestSuite) TestBirdnestMp4Process() { | ||||||
| 	suite.Equal("image/webp", attachment.Thumbnail.ContentType) | 	suite.Equal("image/webp", attachment.Thumbnail.ContentType) | ||||||
| 	suite.Equal(1409625, attachment.File.FileSize) | 	suite.Equal(1409625, attachment.File.FileSize) | ||||||
| 	suite.Equal(14478, attachment.Thumbnail.FileSize) | 	suite.Equal(14478, attachment.Thumbnail.FileSize) | ||||||
| 	suite.Equal("LKF~w1RjRO.99DM_RPaetkV?WCMw", attachment.Blurhash) | 	suite.Equal("LJF?FZV@RO.99DM_RPWAx]V?ayMw", attachment.Blurhash) | ||||||
| 
 | 
 | ||||||
| 	// now make sure the attachment is in the database | 	// now make sure the attachment is in the database | ||||||
| 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | ||||||
|  | @ -657,7 +657,7 @@ func (suite *ManagerTestSuite) TestPngNoAlphaChannelProcess() { | ||||||
| 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType) | 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType) | ||||||
| 	suite.Equal(17471, attachment.File.FileSize) | 	suite.Equal(17471, attachment.File.FileSize) | ||||||
| 	suite.Equal(6446, attachment.Thumbnail.FileSize) | 	suite.Equal(6446, attachment.Thumbnail.FileSize) | ||||||
| 	suite.Equal("LDQcrD%i-?aj%ho#M~RP~nf3~nt2", attachment.Blurhash) | 	suite.Equal("LFQT7e.A%O%4?co$M}M{_1W9~TxV", attachment.Blurhash) | ||||||
| 
 | 
 | ||||||
| 	// now make sure the attachment is in the database | 	// now make sure the attachment is in the database | ||||||
| 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | ||||||
|  | @ -713,7 +713,7 @@ func (suite *ManagerTestSuite) TestPngAlphaChannelProcess() { | ||||||
| 	suite.Equal("image/webp", attachment.Thumbnail.ContentType) | 	suite.Equal("image/webp", attachment.Thumbnail.ContentType) | ||||||
| 	suite.Equal(18832, attachment.File.FileSize) | 	suite.Equal(18832, attachment.File.FileSize) | ||||||
| 	suite.Equal(3592, attachment.Thumbnail.FileSize) | 	suite.Equal(3592, attachment.Thumbnail.FileSize) | ||||||
| 	suite.Equal("LBOW$@%i-rak%go#RSRP_1av~Ts+", attachment.Blurhash) | 	suite.Equal("LCONII.A%Oxw?co#M}M{_1ac~TxV", attachment.Blurhash) | ||||||
| 
 | 
 | ||||||
| 	// now make sure the attachment is in the database | 	// now make sure the attachment is in the database | ||||||
| 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | ||||||
|  | @ -769,7 +769,7 @@ func (suite *ManagerTestSuite) TestSimpleJpegProcessWithCallback() { | ||||||
| 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType) | 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType) | ||||||
| 	suite.Equal(269739, attachment.File.FileSize) | 	suite.Equal(269739, attachment.File.FileSize) | ||||||
| 	suite.Equal(22858, attachment.Thumbnail.FileSize) | 	suite.Equal(22858, attachment.Thumbnail.FileSize) | ||||||
| 	suite.Equal("LjCGfG#6RkRn_NvzRjWF?urqV@a$", attachment.Blurhash) | 	suite.Equal("LiBzRk#6V[WF_NvzV@WY_3rqV@a$", attachment.Blurhash) | ||||||
| 
 | 
 | ||||||
| 	// now make sure the attachment is in the database | 	// now make sure the attachment is in the database | ||||||
| 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | ||||||
|  | @ -847,7 +847,7 @@ func (suite *ManagerTestSuite) TestSimpleJpegProcessWithDiskStorage() { | ||||||
| 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType) | 	suite.Equal("image/jpeg", attachment.Thumbnail.ContentType) | ||||||
| 	suite.Equal(269739, attachment.File.FileSize) | 	suite.Equal(269739, attachment.File.FileSize) | ||||||
| 	suite.Equal(22858, attachment.Thumbnail.FileSize) | 	suite.Equal(22858, attachment.Thumbnail.FileSize) | ||||||
| 	suite.Equal("LjCGfG#6RkRn_NvzRjWF?urqV@a$", attachment.Blurhash) | 	suite.Equal("LiBzRk#6V[WF_NvzV@WY_3rqV@a$", attachment.Blurhash) | ||||||
| 
 | 
 | ||||||
| 	// now make sure the attachment is in the database | 	// now make sure the attachment is in the database | ||||||
| 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | 	dbAttachment, err := suite.db.GetAttachmentByID(ctx, attachment.ID) | ||||||
|  |  | ||||||
|  | @ -34,6 +34,40 @@ import ( | ||||||
| 	"golang.org/x/image/webp" | 	"golang.org/x/image/webp" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | const ( | ||||||
|  | 	maxThumbWidth  = 512 | ||||||
|  | 	maxThumbHeight = 512 | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // thumbSize returns the dimensions to use for an input | ||||||
|  | // image of given width / height, for its outgoing thumbnail. | ||||||
|  | // This attempts to maintains the original image aspect ratio. | ||||||
|  | func thumbSize(width, height int, aspect float32) (int, int) { | ||||||
|  | 
 | ||||||
|  | 	switch { | ||||||
|  | 	// Simplest case, within bounds! | ||||||
|  | 	case width < maxThumbWidth && | ||||||
|  | 		height < maxThumbHeight: | ||||||
|  | 		return width, height | ||||||
|  | 
 | ||||||
|  | 	// Width is larger side. | ||||||
|  | 	case width > height: | ||||||
|  | 		// i.e. height = newWidth * (height / width) | ||||||
|  | 		height = int(float32(maxThumbWidth) / aspect) | ||||||
|  | 		return maxThumbWidth, height | ||||||
|  | 
 | ||||||
|  | 	// Height is larger side. | ||||||
|  | 	case height > width: | ||||||
|  | 		// i.e. width = newHeight * (width / height) | ||||||
|  | 		width = int(float32(maxThumbHeight) * aspect) | ||||||
|  | 		return width, maxThumbHeight | ||||||
|  | 
 | ||||||
|  | 	// Square. | ||||||
|  | 	default: | ||||||
|  | 		return maxThumbWidth, maxThumbHeight | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // generateThumb generates a thumbnail for the | // generateThumb generates a thumbnail for the | ||||||
| // input file at path, resizing it to the given | // input file at path, resizing it to the given | ||||||
| // dimensions and generating a blurhash if needed. | // dimensions and generating a blurhash if needed. | ||||||
|  | @ -229,11 +263,17 @@ func generateNativeThumb( | ||||||
| 		img = imaging.Transverse(img) | 		img = imaging.Transverse(img) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Resize image to dimens. | 	// Resize image to dimens only if necessary. | ||||||
|  | 	if img.Bounds().Dx() > maxThumbWidth || | ||||||
|  | 		img.Bounds().Dy() > maxThumbHeight { | ||||||
|  | 		// Note: We could call "imaging.Fit" here | ||||||
|  | 		// but there's no point, as we've already | ||||||
|  | 		// calculated target dimensions beforehand. | ||||||
| 		img = imaging.Resize(img, | 		img = imaging.Resize(img, | ||||||
| 			width, height, | 			width, height, | ||||||
| 			imaging.Linear, | 			imaging.Linear, | ||||||
| 		) | 		) | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	// Open output file at given path. | 	// Open output file at given path. | ||||||
| 	outfile, err := os.Create(outpath) | 	outfile, err := os.Create(outpath) | ||||||
|  | @ -255,7 +295,7 @@ func generateNativeThumb( | ||||||
| 	if needBlurhash { | 	if needBlurhash { | ||||||
| 		// for generating blurhashes, it's more cost effective to | 		// for generating blurhashes, it's more cost effective to | ||||||
| 		// lose detail since it's blurry, so make a tiny version. | 		// lose detail since it's blurry, so make a tiny version. | ||||||
| 		tiny := imaging.Resize(img, 64, 64, imaging.NearestNeighbor) | 		tiny := imaging.Resize(img, 32, 0, imaging.NearestNeighbor) | ||||||
| 
 | 
 | ||||||
| 		// Drop the larger image | 		// Drop the larger image | ||||||
| 		// ref as soon as possible | 		// ref as soon as possible | ||||||
|  | @ -294,7 +334,7 @@ func generateWebpBlurhash(filepath string) (string, error) { | ||||||
| 
 | 
 | ||||||
| 	// for generating blurhashes, it's more cost effective to | 	// for generating blurhashes, it's more cost effective to | ||||||
| 	// lose detail since it's blurry, so make a tiny version. | 	// lose detail since it's blurry, so make a tiny version. | ||||||
| 	tiny := imaging.Resize(img, 64, 64, imaging.NearestNeighbor) | 	tiny := imaging.Resize(img, 32, 0, imaging.NearestNeighbor) | ||||||
| 
 | 
 | ||||||
| 	// Drop the larger image | 	// Drop the larger image | ||||||
| 	// ref as soon as possible | 	// ref as soon as possible | ||||||
|  |  | ||||||
|  | @ -39,39 +39,6 @@ func getExtension(path string) string { | ||||||
| 	return "" | 	return "" | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // thumbSize returns the dimensions to use for an input |  | ||||||
| // image of given width / height, for its outgoing thumbnail. |  | ||||||
| // This attempts to maintains the original image aspect ratio. |  | ||||||
| func thumbSize(width, height int, aspect float32) (int, int) { |  | ||||||
| 	const ( |  | ||||||
| 		maxThumbWidth  = 512 |  | ||||||
| 		maxThumbHeight = 512 |  | ||||||
| 	) |  | ||||||
| 
 |  | ||||||
| 	switch { |  | ||||||
| 	// Simplest case, within bounds! |  | ||||||
| 	case width < maxThumbWidth && |  | ||||||
| 		height < maxThumbHeight: |  | ||||||
| 		return width, height |  | ||||||
| 
 |  | ||||||
| 	// Width is larger side. |  | ||||||
| 	case width > height: |  | ||||||
| 		// i.e. height = newWidth * (height / width) |  | ||||||
| 		height = int(float32(maxThumbWidth) / aspect) |  | ||||||
| 		return maxThumbWidth, height |  | ||||||
| 
 |  | ||||||
| 	// Height is larger side. |  | ||||||
| 	case height > width: |  | ||||||
| 		// i.e. width = newHeight * (width / height) |  | ||||||
| 		width = int(float32(maxThumbHeight) * aspect) |  | ||||||
| 		return width, maxThumbHeight |  | ||||||
| 
 |  | ||||||
| 	// Square. |  | ||||||
| 	default: |  | ||||||
| 		return maxThumbWidth, maxThumbHeight |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // getMimeType returns a suitable mimetype for file extension. | // getMimeType returns a suitable mimetype for file extension. | ||||||
| func getMimeType(ext string) string { | func getMimeType(ext string) string { | ||||||
| 	const defaultType = "application/octet-stream" | 	const defaultType = "application/octet-stream" | ||||||
|  |  | ||||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB | 
|  | @ -1078,7 +1078,7 @@ func NewTestAttachments() map[string]*gtsmodel.MediaAttachment { | ||||||
| 			Thumbnail: gtsmodel.Thumbnail{ | 			Thumbnail: gtsmodel.Thumbnail{ | ||||||
| 				Path:        "01F8MH5ZK5VRH73AKHQM6Y9VNX/attachment/small/01FVW7RXPQ8YJHTEXYPE7Q8ZY0.jpeg", | 				Path:        "01F8MH5ZK5VRH73AKHQM6Y9VNX/attachment/small/01FVW7RXPQ8YJHTEXYPE7Q8ZY0.jpeg", | ||||||
| 				ContentType: "image/jpeg", | 				ContentType: "image/jpeg", | ||||||
| 				FileSize:    20394, | 				FileSize:    20395, | ||||||
| 				URL:         "http://localhost:8080/fileserver/01F8MH5ZK5VRH73AKHQM6Y9VNX/attachment/small/01FVW7RXPQ8YJHTEXYPE7Q8ZY0.webp", | 				URL:         "http://localhost:8080/fileserver/01F8MH5ZK5VRH73AKHQM6Y9VNX/attachment/small/01FVW7RXPQ8YJHTEXYPE7Q8ZY0.webp", | ||||||
| 			}, | 			}, | ||||||
| 			Avatar: util.Ptr(false), | 			Avatar: util.Ptr(false), | ||||||
|  | @ -1124,7 +1124,7 @@ func NewTestAttachments() map[string]*gtsmodel.MediaAttachment { | ||||||
| 			Thumbnail: gtsmodel.Thumbnail{ | 			Thumbnail: gtsmodel.Thumbnail{ | ||||||
| 				Path:        "062G5WYKY35KKD12EMSM3F8PJ8/attachment/small/01PFPMWK2FF0D9WMHEJHR07C3R.jpeg", | 				Path:        "062G5WYKY35KKD12EMSM3F8PJ8/attachment/small/01PFPMWK2FF0D9WMHEJHR07C3R.jpeg", | ||||||
| 				ContentType: "image/webp", | 				ContentType: "image/webp", | ||||||
| 				FileSize:    20394, | 				FileSize:    20395, | ||||||
| 				URL:         "http://localhost:8080/fileserver/062G5WYKY35KKD12EMSM3F8PJ8/header/small/01PFPMWK2FF0D9WMHEJHR07C3R.webp", | 				URL:         "http://localhost:8080/fileserver/062G5WYKY35KKD12EMSM3F8PJ8/header/small/01PFPMWK2FF0D9WMHEJHR07C3R.webp", | ||||||
| 			}, | 			}, | ||||||
| 			Avatar: util.Ptr(false), | 			Avatar: util.Ptr(false), | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue