diff --git a/Dotnet/AppApi/AppApi.cs b/Dotnet/AppApi/AppApi.cs index 978eff2f..1d4ae12b 100644 --- a/Dotnet/AppApi/AppApi.cs +++ b/Dotnet/AppApi/AppApi.cs @@ -61,16 +61,17 @@ namespace VRCX public string ResizeImageToFitLimits(string base64data) { - return Convert.ToBase64String(ResizeImageToFitLimits(Convert.FromBase64String(base64data))); + return Convert.ToBase64String(ResizeImageToFitLimits(Convert.FromBase64String(base64data), false)); } - public byte[] ResizeImageToFitLimits(byte[] imageData, int maxWidth = 2000, int maxHeight = 2000, long maxSize = 10_000_000) + public byte[] ResizeImageToFitLimits(byte[] imageData, bool matchingDimensions, int maxWidth = 2000, int maxHeight = 2000, long maxSize = 10_000_000) { using var fileMemoryStream = new MemoryStream(imageData); var image = new Bitmap(fileMemoryStream); // for APNG, check if image is png format and less than maxSize - if (image.RawFormat.Equals(System.Drawing.Imaging.ImageFormat.Png) && + if ((!matchingDimensions || image.Width == image.Height) && + image.RawFormat.Equals(System.Drawing.Imaging.ImageFormat.Png) && imageData.Length < maxSize && image.Width <= maxWidth && image.Height <= maxHeight) @@ -90,7 +91,19 @@ namespace VRCX var newWidth = (int)Math.Round(image.Width / sizingFactor); image = new Bitmap(image, newWidth, maxHeight); } - + if (matchingDimensions && image.Width != image.Height) + { + var newSize = Math.Max(image.Width, image.Height); + var newImage = new Bitmap(newSize, newSize); + using (var graphics = Graphics.FromImage(newImage)) + { + graphics.Clear(Color.Transparent); + graphics.DrawImage(image, new Rectangle((newSize - image.Width) / 2, (newSize - image.Height) / 2, image.Width, image.Height)); + } + image.Dispose(); + image = newImage; + } + SaveToFileToUpload(); for (int i = 0; i < 250 && imageData.Length > maxSize; i++) { diff --git a/Dotnet/WebApi.cs b/Dotnet/WebApi.cs index eb841071..3b42b258 100644 --- a/Dotnet/WebApi.cs +++ b/Dotnet/WebApi.cs @@ -200,7 +200,7 @@ namespace VRCX } } var imageData = options["imageData"] as string; - byte[] fileToUpload = AppApi.Instance.ResizeImageToFitLimits(Convert.FromBase64String(imageData)); + byte[] fileToUpload = AppApi.Instance.ResizeImageToFitLimits(Convert.FromBase64String(imageData), false); string fileFormKey = "image"; string fileName = "image.png"; string fileMimeType = "image/png"; @@ -268,7 +268,8 @@ namespace VRCX } } var imageData = options["imageData"] as string; - byte[] fileToUpload = AppApi.Instance.ResizeImageToFitLimits(Convert.FromBase64String(imageData)); + var matchingDimensions = options["matchingDimensions"] as bool? ?? false; + byte[] fileToUpload = AppApi.Instance.ResizeImageToFitLimits(Convert.FromBase64String(imageData), matchingDimensions); string fileFormKey = "file"; string fileName = "blob"; @@ -305,7 +306,7 @@ namespace VRCX request.ContentType = "multipart/form-data; boundary=" + boundary; var requestStream = request.GetRequestStream(); var imageData = options["imageData"] as string; - var fileToUpload = AppApi.Instance.ResizeImageToFitLimits(Convert.FromBase64String(imageData), 1920, 1080); + var fileToUpload = AppApi.Instance.ResizeImageToFitLimits(Convert.FromBase64String(imageData), false, 1920, 1080); const string fileFormKey = "image"; const string fileName = "image"; const string fileMimeType = "image/png"; diff --git a/html/src/app.js b/html/src/app.js index 7bff114b..06c3071d 100644 --- a/html/src/app.js +++ b/html/src/app.js @@ -13651,6 +13651,7 @@ speechSynthesis.getVoices(); }; return this.call('file/image', { uploadImage: true, + matchingDimensions: true, postData: JSON.stringify(params), imageData }).then((json) => { @@ -17334,6 +17335,7 @@ speechSynthesis.getVoices(); }; return this.call('file/image', { uploadImage: true, + matchingDimensions: false, postData: JSON.stringify(params), imageData }).then((json) => { @@ -17445,6 +17447,7 @@ speechSynthesis.getVoices(); API.uploadSticker = function (imageData, params) { return this.call('file/image', { uploadImage: true, + matchingDimensions: true, postData: JSON.stringify(params), imageData }).then((json) => { @@ -17762,6 +17765,7 @@ speechSynthesis.getVoices(); API.uploadEmoji = function (imageData, params) { return this.call('file/image', { uploadImage: true, + matchingDimensions: true, postData: JSON.stringify(params), imageData }).then((json) => { diff --git a/html/src/mixins/dialogs/currentUser.pug b/html/src/mixins/dialogs/currentUser.pug index 2f90e2db..e7d60620 100644 --- a/html/src/mixins/dialogs/currentUser.pug +++ b/html/src/mixins/dialogs/currentUser.pug @@ -65,7 +65,7 @@ mixin currentUser() el-tab-pane(v-if="galleryDialogVisible" v-loading="galleryDialogGalleryLoading") span(slot="label") {{ $t('dialog.gallery_icons.gallery') }} span(style="color:#909399;font-size:12px;margin-left:5px") {{ galleryTable.length }}/64 - input(type="file" accept="image/png,image/jpg,image/jpeg,image/webp,image/bmp,image/gif" @change="onFileChangeGallery" id="GalleryUploadButton" style="display:none") + input(type="file" accept="image/*" @change="onFileChangeGallery" id="GalleryUploadButton" style="display:none") span {{ $t('dialog.gallery_icons.recommended_image_size') }}: 1200x900px (4:3) br br @@ -83,8 +83,8 @@ mixin currentUser() el-tab-pane(v-if="galleryDialogVisible" v-loading="galleryDialogIconsLoading") span(slot="label") {{ $t('dialog.gallery_icons.icons') }} span(style="color:#909399;font-size:12px;margin-left:5px") {{ VRCPlusIconsTable.length }}/64 - input(type="file" accept="image/png,image/jpg,image/jpeg,image/webp,image/bmp,image/gif" @change="onFileChangeVRCPlusIcon" id="VRCPlusIconUploadButton" style="display:none") - span {{ $t('dialog.gallery_icons.recommended_image_size') }}: 1200x900px (4:3) + input(type="file" accept="image/*" @change="onFileChangeVRCPlusIcon" id="VRCPlusIconUploadButton" style="display:none") + span {{ $t('dialog.gallery_icons.recommended_image_size') }}: 2000x2000px (1:1) br br el-button-group @@ -101,8 +101,8 @@ mixin currentUser() el-tab-pane(v-if="galleryDialogVisible" v-loading="galleryDialogEmojisLoading") span(slot="label") {{ $t('dialog.gallery_icons.emojis') }} span(style="color:#909399;font-size:12px;margin-left:5px") {{ emojiTable.length }}/9 - input(type="file" accept="image/png,image/jpg,image/jpeg,image/webp,image/bmp,image/gif" @change="onFileChangeEmoji" id="EmojiUploadButton" style="display:none") - span {{ $t('dialog.gallery_icons.recommended_image_size') }}: 1200x900px (4:3) + input(type="file" accept="image/*" @change="onFileChangeEmoji" id="EmojiUploadButton" style="display:none") + span {{ $t('dialog.gallery_icons.recommended_image_size') }}: 2000x2000px (1:1) br br el-button-group(style="margin-right:10px") @@ -145,8 +145,8 @@ mixin currentUser() el-tab-pane(v-if="galleryDialogVisible" v-loading="galleryDialogStickersLoading") span(slot="label") {{ $t('dialog.gallery_icons.stickers') }} span(style="color:#909399;font-size:12px;margin-left:5px") {{ stickerTable.length }}/9 - input(type="file" accept="image/png,image/jpg,image/jpeg,image/webp,image/bmp,image/gif" @change="onFileChangeSticker" id="StickerUploadButton" style="display:none") - span {{ $t('dialog.gallery_icons.recommended_image_size') }}: 1200x900px (4:3) + input(type="file" accept="image/*" @change="onFileChangeSticker" id="StickerUploadButton" style="display:none") + span {{ $t('dialog.gallery_icons.recommended_image_size') }}: 2000x2000px (1:1) br br el-button-group @@ -162,7 +162,7 @@ mixin currentUser() el-tab-pane(v-if="galleryDialogVisible" v-loading="galleryDialogPrintsLoading") span(slot="label") {{ $t('dialog.gallery_icons.prints') }} span(style="color:#909399;font-size:12px;margin-left:5px") {{ printTable.length }}/64 - input(type="file" accept="image/png,image/jpg,image/jpeg,image/webp,image/bmp,image/gif" @change="onFileChangePrint" id="PrintUploadButton" style="display:none") + input(type="file" accept="image/*" @change="onFileChangePrint" id="PrintUploadButton" style="display:none") span {{ $t('dialog.gallery_icons.recommended_image_size') }}: 1920x1080px (16:9) br br diff --git a/html/src/mixins/dialogs/images.pug b/html/src/mixins/dialogs/images.pug index 903c71e1..c013202c 100644 --- a/html/src/mixins/dialogs/images.pug +++ b/html/src/mixins/dialogs/images.pug @@ -2,7 +2,7 @@ mixin images() //- dialog: Change avatar image el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="changeAvatarImageDialog" :visible.sync="changeAvatarImageDialogVisible" :title="$t('dialog.change_content_image.avatar')" width="850px") div(v-if="changeAvatarImageDialogVisible" v-loading="changeAvatarImageDialogLoading") - input(type="file" accept="image/png,image/jpg,image/jpeg,image/webp,image/bmp,image/gif" @change="onFileChangeAvatarImage" id="AvatarImageUploadButton" style="display:none") + input(type="file" accept="image/*" @change="onFileChangeAvatarImage" id="AvatarImageUploadButton" style="display:none") span {{ $t('dialog.change_content_image.description') }} br el-button-group(style="padding-bottom:10px;padding-top:10px") @@ -17,7 +17,7 @@ mixin images() //- dialog: Change world image el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="changeWorldImageDialog" :visible.sync="changeWorldImageDialogVisible" :title="$t('dialog.change_content_image.world')" width="850px") div(v-if="changeWorldImageDialogVisible" v-loading="changeWorldImageDialogLoading") - input(type="file" accept="image/png,image/jpg,image/jpeg,image/webp,image/bmp,image/gif" @change="onFileChangeWorldImage" id="WorldImageUploadButton" style="display:none") + input(type="file" accept="image/*" @change="onFileChangeWorldImage" id="WorldImageUploadButton" style="display:none") span {{ $t('dialog.change_content_image.description') }} br el-button-group(style="padding-bottom:10px;padding-top:10px") @@ -43,7 +43,7 @@ mixin images() span(slot="label") {{ $t('dialog.gallery_select.gallery') }} span(style="color:#909399;font-size:12px;margin-left:5px") {{ galleryTable.length }}/64 br - input(type="file" accept="image/png,image/jpg,image/jpeg,image/webp,image/bmp,image/gif" @change="onFileChangeGallery" id="GalleryUploadButton" style="display:none") + input(type="file" accept="image/*" @change="onFileChangeGallery" id="GalleryUploadButton" style="display:none") el-button-group el-button(type="default" size="small" @click="selectImageGallerySelect('', '')" icon="el-icon-close") {{ $t('dialog.gallery_select.none') }} el-button(type="default" size="small" @click="refreshGalleryTable" icon="el-icon-refresh") {{ $t('dialog.gallery_select.refresh') }} diff --git a/html/src/mixins/dialogs/invites.pug b/html/src/mixins/dialogs/invites.pug index d04fc879..b29e9f5f 100644 --- a/html/src/mixins/dialogs/invites.pug +++ b/html/src/mixins/dialogs/invites.pug @@ -71,7 +71,7 @@ mixin invites() //- dialog Table: Send Invite Response Message el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="sendInviteResponseDialog" :visible.sync="sendInviteResponseDialogVisible" :title="$t('dialog.invite_response_message.header')" width="800px") template(v-if="API.currentUser.$isVRCPlus") - input.inviteImageUploadButton(type="file" accept="image/png,image/jpg,image/jpeg,image/webp,image/bmp,image/gif" @change="inviteImageUpload") + input.inviteImageUploadButton(type="file" accept="image/*" @change="inviteImageUpload") data-tables(v-if="sendInviteResponseDialogVisible" v-bind="inviteResponseMessageTable" @row-click="showSendInviteResponseConfirmDialog" style="margin-top:10px;cursor:pointer") el-table-column(:label="$t('table.profile.invite_messages.slot')" prop="slot" sortable="custom" width="70") el-table-column(:label="$t('table.profile.invite_messages.message')" prop="message") @@ -88,7 +88,7 @@ mixin invites() //- dialog Table: Send Invite Request Response Message el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="sendInviteRequestResponseDialog" :visible.sync="sendInviteRequestResponseDialogVisible" :title="$t('dialog.invite_request_response_message.header')" width="800px") template(v-if="API.currentUser.$isVRCPlus") - input.inviteImageUploadButton(type="file" accept="image/png,image/jpg,image/jpeg,image/webp,image/bmp,image/gif" @change="inviteImageUpload") + input.inviteImageUploadButton(type="file" accept="image/*" @change="inviteImageUpload") data-tables(v-if="sendInviteRequestResponseDialogVisible" v-bind="inviteRequestResponseMessageTable" @row-click="showSendInviteResponseConfirmDialog" style="margin-top:10px;cursor:pointer") el-table-column(:label="$t('table.profile.invite_messages.slot')" prop="slot" sortable="custom" width="70") el-table-column(:label="$t('table.profile.invite_messages.message')" prop="message") @@ -121,7 +121,7 @@ mixin invites() //- el-button(size="mini" @click="clearImageGallerySelect" style="vertical-align:top") {{ $t('dialog.invite_message.clear_selected_image') }} //- template(v-else) //- el-button(size="mini" @click="showGallerySelectDialog" style="margin-right:5px") {{ $t('dialog.invite_message.select_image') }} - input.inviteImageUploadButton(type="file" accept="image/png,image/jpg,image/jpeg,image/webp,image/bmp,image/gif" @change="inviteImageUpload") + input.inviteImageUploadButton(type="file" accept="image/*" @change="inviteImageUpload") data-tables(v-if="sendInviteDialogVisible" v-bind="inviteMessageTable" @row-click="showSendInviteConfirmDialog" style="margin-top:10px;cursor:pointer") el-table-column(:label="$t('table.profile.invite_messages.slot')" prop="slot" sortable="custom" width="70") el-table-column(:label="$t('table.profile.invite_messages.message')" prop="message") @@ -138,7 +138,7 @@ mixin invites() //- dialog Table: Send Invite Request Message el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="sendInviteRequestDialog" :visible.sync="sendInviteRequestDialogVisible" :title="$t('dialog.invite_request_message.header')" width="800px") template(v-if="API.currentUser.$isVRCPlus") - input.inviteImageUploadButton(type="file" accept="image/png,image/jpg,image/jpeg,image/webp,image/bmp,image/gif" @change="inviteImageUpload") + input.inviteImageUploadButton(type="file" accept="image/*" @change="inviteImageUpload") data-tables(v-if="sendInviteRequestDialogVisible" v-bind="inviteRequestMessageTable" @row-click="showSendInviteConfirmDialog" style="margin-top:10px;cursor:pointer") el-table-column(:label="$t('table.profile.invite_messages.slot')" prop="slot" sortable="custom" width="70") el-table-column(:label="$t('table.profile.invite_messages.message')" prop="message")