mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-05-07 14:56:06 +02:00
Automatically resize images to fit limits (#831)
Resize images for Avatars, Worlds, Gallery, Icons and Emojis when uploading if it exceeds VRChats limits (2000 pixels in any dimension and 10MB file size).
This commit is contained in:
@@ -60,6 +60,65 @@ namespace VRCX
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string ResizeImageToFitLimits(string base64data)
|
||||||
|
{
|
||||||
|
return Convert.ToBase64String(ResizeImageToFitLimits(Convert.FromBase64String(base64data)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] ResizeImageToFitLimits(byte[] imageData, int maxWidth = 2000, int maxHeight = 2000, long maxSize = 10_000_000)
|
||||||
|
{
|
||||||
|
using var fileMemoryStream = new MemoryStream(imageData);
|
||||||
|
System.Drawing.Bitmap image = new System.Drawing.Bitmap(fileMemoryStream);
|
||||||
|
if (image.Width > maxWidth)
|
||||||
|
{
|
||||||
|
var sizingFactor = image.Width / (double)maxWidth;
|
||||||
|
int newHeight = (int)Math.Round(image.Height / sizingFactor);
|
||||||
|
image = new System.Drawing.Bitmap(image, maxWidth, newHeight);
|
||||||
|
}
|
||||||
|
if (image.Height > maxHeight)
|
||||||
|
{
|
||||||
|
var sizingFactor = image.Height / (double)maxHeight;
|
||||||
|
int newWidth = (int)Math.Round(image.Width / sizingFactor);
|
||||||
|
image = new System.Drawing.Bitmap(image, newWidth, maxHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveToFileToUpload()
|
||||||
|
{
|
||||||
|
using var imageSaveMemoryStream = new MemoryStream();
|
||||||
|
image.Save(imageSaveMemoryStream, System.Drawing.Imaging.ImageFormat.Png);
|
||||||
|
imageData = imageSaveMemoryStream.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
saveToFileToUpload();
|
||||||
|
|
||||||
|
for (int i = 0; i < 250 && imageData.Length > maxSize; i++)
|
||||||
|
{
|
||||||
|
saveToFileToUpload();
|
||||||
|
if (imageData.Length < maxSize)
|
||||||
|
break;
|
||||||
|
int newWidth = image.Width;
|
||||||
|
int newHeight = image.Height;
|
||||||
|
if (image.Width > image.Height)
|
||||||
|
{
|
||||||
|
newWidth = image.Width - 25;
|
||||||
|
newHeight = (int)Math.Round(image.Height / (image.Width / (double)newWidth));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newHeight = image.Height - 25;
|
||||||
|
newWidth = (int)Math.Round(image.Width / (image.Height / (double)newHeight));
|
||||||
|
}
|
||||||
|
image = new System.Drawing.Bitmap(image, newWidth, newHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (imageData.Length > maxSize)
|
||||||
|
{
|
||||||
|
throw new Exception("Failed to get image into target filesize.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return imageData;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Computes the signature of the file represented by the specified base64-encoded string using the librsync library.
|
/// Computes the signature of the file represented by the specified base64-encoded string using the librsync library.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
+2
-1
@@ -217,7 +217,8 @@ namespace VRCX
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
var imageData = options["imageData"] as string;
|
var imageData = options["imageData"] as string;
|
||||||
byte[] fileToUpload = Convert.FromBase64CharArray(imageData.ToCharArray(), 0, imageData.Length);
|
byte[] fileToUpload = AppApi.Instance.ResizeImageToFitLimits(Convert.FromBase64String(imageData));
|
||||||
|
|
||||||
string fileFormKey = "file";
|
string fileFormKey = "file";
|
||||||
string fileName = "blob";
|
string fileName = "blob";
|
||||||
string fileMimeType = "image/png";
|
string fileMimeType = "image/png";
|
||||||
|
|||||||
+24
-17
@@ -21598,8 +21598,8 @@ speechSynthesis.getVoices();
|
|||||||
if (!files.length) {
|
if (!files.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (files[0].size >= 10000000) {
|
if (files[0].size >= 100000000) {
|
||||||
// 10MB
|
// 100MB
|
||||||
$app.$message({
|
$app.$message({
|
||||||
message: 'File size too large',
|
message: 'File size too large',
|
||||||
type: 'error'
|
type: 'error'
|
||||||
@@ -21665,8 +21665,8 @@ speechSynthesis.getVoices();
|
|||||||
if (!files.length) {
|
if (!files.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (files[0].size >= 10000000) {
|
if (files[0].size >= 100000000) {
|
||||||
// 10MB
|
// 100MB
|
||||||
$app.$message({
|
$app.$message({
|
||||||
message: 'File size too large',
|
message: 'File size too large',
|
||||||
type: 'error'
|
type: 'error'
|
||||||
@@ -21674,7 +21674,7 @@ speechSynthesis.getVoices();
|
|||||||
this.clearInviteImageUpload();
|
this.clearInviteImageUpload();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!files[0].type.match(/image.png/)) {
|
if (!files[0].type.match(/image.*/)) {
|
||||||
$app.$message({
|
$app.$message({
|
||||||
message: "File isn't a png",
|
message: "File isn't a png",
|
||||||
type: 'error'
|
type: 'error'
|
||||||
@@ -22679,6 +22679,11 @@ speechSynthesis.getVoices();
|
|||||||
return response;
|
return response;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$app.methods.resizeImageToFitLimits = async function (file) {
|
||||||
|
var response = await AppApi.ResizeImageToFitLimits(file);
|
||||||
|
return response;
|
||||||
|
};
|
||||||
|
|
||||||
$app.methods.genSig = async function (file) {
|
$app.methods.genSig = async function (file) {
|
||||||
var response = await AppApi.SignFile(file);
|
var response = await AppApi.SignFile(file);
|
||||||
return response;
|
return response;
|
||||||
@@ -22706,8 +22711,8 @@ speechSynthesis.getVoices();
|
|||||||
clearFile();
|
clearFile();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (files[0].size >= 10000000) {
|
if (files[0].size >= 100000000) {
|
||||||
// 10MB
|
// 100MB
|
||||||
$app.$message({
|
$app.$message({
|
||||||
message: 'File size too large',
|
message: 'File size too large',
|
||||||
type: 'error'
|
type: 'error'
|
||||||
@@ -22715,7 +22720,7 @@ speechSynthesis.getVoices();
|
|||||||
clearFile();
|
clearFile();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!files[0].type.match(/image.png/)) {
|
if (!files[0].type.match(/image.*/)) {
|
||||||
$app.$message({
|
$app.$message({
|
||||||
message: "File isn't a png",
|
message: "File isn't a png",
|
||||||
type: 'error'
|
type: 'error'
|
||||||
@@ -22727,7 +22732,8 @@ speechSynthesis.getVoices();
|
|||||||
this.changeAvatarImageDialogLoading = true;
|
this.changeAvatarImageDialogLoading = true;
|
||||||
var r = new FileReader();
|
var r = new FileReader();
|
||||||
r.onload = async function (file) {
|
r.onload = async function (file) {
|
||||||
var base64File = btoa(r.result);
|
var base64File = await $app.resizeImageToFitLimits(btoa(r.result));
|
||||||
|
// 10MB
|
||||||
var fileMd5 = await $app.genMd5(base64File);
|
var fileMd5 = await $app.genMd5(base64File);
|
||||||
var fileSizeInBytes = parseInt(file.total, 10);
|
var fileSizeInBytes = parseInt(file.total, 10);
|
||||||
var base64SignatureFile = await $app.genSig(base64File);
|
var base64SignatureFile = await $app.genSig(base64File);
|
||||||
@@ -23041,8 +23047,8 @@ speechSynthesis.getVoices();
|
|||||||
clearFile();
|
clearFile();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (files[0].size >= 10000000) {
|
if (files[0].size >= 100000000) {
|
||||||
// 10MB
|
// 100MB
|
||||||
$app.$message({
|
$app.$message({
|
||||||
message: 'File size too large',
|
message: 'File size too large',
|
||||||
type: 'error'
|
type: 'error'
|
||||||
@@ -23050,7 +23056,7 @@ speechSynthesis.getVoices();
|
|||||||
clearFile();
|
clearFile();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!files[0].type.match(/image.png/)) {
|
if (!files[0].type.match(/image.*/)) {
|
||||||
$app.$message({
|
$app.$message({
|
||||||
message: "File isn't a png",
|
message: "File isn't a png",
|
||||||
type: 'error'
|
type: 'error'
|
||||||
@@ -23062,7 +23068,8 @@ speechSynthesis.getVoices();
|
|||||||
this.changeWorldImageDialogLoading = true;
|
this.changeWorldImageDialogLoading = true;
|
||||||
var r = new FileReader();
|
var r = new FileReader();
|
||||||
r.onload = async function (file) {
|
r.onload = async function (file) {
|
||||||
var base64File = btoa(r.result);
|
var base64File = await $app.resizeImageToFitLimits(btoa(r.result));
|
||||||
|
// 10MB
|
||||||
var fileMd5 = await $app.genMd5(base64File);
|
var fileMd5 = await $app.genMd5(base64File);
|
||||||
var fileSizeInBytes = parseInt(file.total, 10);
|
var fileSizeInBytes = parseInt(file.total, 10);
|
||||||
var base64SignatureFile = await $app.genSig(base64File);
|
var base64SignatureFile = await $app.genSig(base64File);
|
||||||
@@ -25073,8 +25080,8 @@ speechSynthesis.getVoices();
|
|||||||
if (!files.length) {
|
if (!files.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (files[0].size >= 10000000) {
|
if (files[0].size >= 100000000) {
|
||||||
// 10MB
|
// 100MB
|
||||||
$app.$message({
|
$app.$message({
|
||||||
message: 'File size too large',
|
message: 'File size too large',
|
||||||
type: 'error'
|
type: 'error'
|
||||||
@@ -25184,8 +25191,8 @@ speechSynthesis.getVoices();
|
|||||||
if (!files.length) {
|
if (!files.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (files[0].size >= 10000000) {
|
if (files[0].size >= 100000000) {
|
||||||
// 10MB
|
// 100MB
|
||||||
$app.$message({
|
$app.$message({
|
||||||
message: 'File size too large',
|
message: 'File size too large',
|
||||||
type: 'error'
|
type: 'error'
|
||||||
|
|||||||
+6
-6
@@ -2428,7 +2428,7 @@ html
|
|||||||
//- dialog: Change avatar image
|
//- 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")
|
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")
|
div(v-if="changeAvatarImageDialogVisible" v-loading="changeAvatarImageDialogLoading")
|
||||||
input(type="file" accept="image/*" @change="onFileChangeAvatarImage" id="AvatarImageUploadButton" style="display:none")
|
input(type="file" accept="image/png,image/jpg,image/jpeg,image/webp,image/bmp,image/gif" @change="onFileChangeAvatarImage" id="AvatarImageUploadButton" style="display:none")
|
||||||
span {{ $t('dialog.change_content_image.description') }}
|
span {{ $t('dialog.change_content_image.description') }}
|
||||||
br
|
br
|
||||||
el-button-group(style="padding-bottom:10px;padding-top:10px")
|
el-button-group(style="padding-bottom:10px;padding-top:10px")
|
||||||
@@ -2443,7 +2443,7 @@ html
|
|||||||
//- dialog: Change world image
|
//- 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")
|
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")
|
div(v-if="changeWorldImageDialogVisible" v-loading="changeWorldImageDialogLoading")
|
||||||
input(type="file" accept="image/*" @change="onFileChangeWorldImage" id="WorldImageUploadButton" style="display:none")
|
input(type="file" accept="image/png,image/jpg,image/jpeg,image/webp,image/bmp,image/gif" @change="onFileChangeWorldImage" id="WorldImageUploadButton" style="display:none")
|
||||||
span {{ $t('dialog.change_content_image.description') }}
|
span {{ $t('dialog.change_content_image.description') }}
|
||||||
br
|
br
|
||||||
el-button-group(style="padding-bottom:10px;padding-top:10px")
|
el-button-group(style="padding-bottom:10px;padding-top:10px")
|
||||||
@@ -2472,7 +2472,7 @@ html
|
|||||||
el-tab-pane(v-if="galleryDialogVisible" v-loading="galleryDialogGalleryLoading")
|
el-tab-pane(v-if="galleryDialogVisible" v-loading="galleryDialogGalleryLoading")
|
||||||
span(slot="label") {{ $t('dialog.gallery_icons.gallery') }}
|
span(slot="label") {{ $t('dialog.gallery_icons.gallery') }}
|
||||||
span(style="color:#909399;font-size:12px;margin-left:5px") {{ galleryTable.length }}/64
|
span(style="color:#909399;font-size:12px;margin-left:5px") {{ galleryTable.length }}/64
|
||||||
input(type="file" accept="image/*" @change="onFileChangeGallery" id="GalleryUploadButton" style="display:none")
|
input(type="file" accept="image/png,image/jpg,image/jpeg,image/webp,image/bmp,image/gif" @change="onFileChangeGallery" id="GalleryUploadButton" style="display:none")
|
||||||
el-button-group
|
el-button-group
|
||||||
el-button(type="default" size="small" @click="refreshGalleryTable" icon="el-icon-refresh") {{ $t('dialog.gallery_icons.refresh') }}
|
el-button(type="default" size="small" @click="refreshGalleryTable" icon="el-icon-refresh") {{ $t('dialog.gallery_icons.refresh') }}
|
||||||
el-button(type="default" size="small" @click="displayGalleryUpload" icon="el-icon-upload2" :disabled="!API.currentUser.$isVRCPlus") {{ $t('dialog.gallery_icons.upload') }}
|
el-button(type="default" size="small" @click="displayGalleryUpload" icon="el-icon-upload2" :disabled="!API.currentUser.$isVRCPlus") {{ $t('dialog.gallery_icons.upload') }}
|
||||||
@@ -2487,7 +2487,7 @@ html
|
|||||||
el-tab-pane(v-if="galleryDialogVisible" v-loading="galleryDialogIconsLoading")
|
el-tab-pane(v-if="galleryDialogVisible" v-loading="galleryDialogIconsLoading")
|
||||||
span(slot="label") {{ $t('dialog.gallery_icons.icons') }}
|
span(slot="label") {{ $t('dialog.gallery_icons.icons') }}
|
||||||
span(style="color:#909399;font-size:12px;margin-left:5px") {{ VRCPlusIconsTable.length }}/64
|
span(style="color:#909399;font-size:12px;margin-left:5px") {{ VRCPlusIconsTable.length }}/64
|
||||||
input(type="file" accept="image/*" @change="onFileChangeVRCPlusIcon" id="VRCPlusIconUploadButton" style="display:none")
|
input(type="file" accept="image/png,image/jpg,image/jpeg,image/webp,image/bmp,image/gif" @change="onFileChangeVRCPlusIcon" id="VRCPlusIconUploadButton" style="display:none")
|
||||||
el-button-group
|
el-button-group
|
||||||
el-button(type="default" size="small" @click="refreshVRCPlusIconsTable" icon="el-icon-refresh") {{ $t('dialog.gallery_icons.refresh') }}
|
el-button(type="default" size="small" @click="refreshVRCPlusIconsTable" icon="el-icon-refresh") {{ $t('dialog.gallery_icons.refresh') }}
|
||||||
el-button(type="default" size="small" @click="displayVRCPlusIconUpload" icon="el-icon-upload2" :disabled="!API.currentUser.$isVRCPlus") {{ $t('dialog.gallery_icons.upload') }}
|
el-button(type="default" size="small" @click="displayVRCPlusIconUpload" icon="el-icon-upload2" :disabled="!API.currentUser.$isVRCPlus") {{ $t('dialog.gallery_icons.upload') }}
|
||||||
@@ -2502,7 +2502,7 @@ html
|
|||||||
el-tab-pane(v-if="galleryDialogVisible" v-loading="galleryDialogIconsLoading")
|
el-tab-pane(v-if="galleryDialogVisible" v-loading="galleryDialogIconsLoading")
|
||||||
span(slot="label") {{ $t('dialog.gallery_icons.emojis') }}
|
span(slot="label") {{ $t('dialog.gallery_icons.emojis') }}
|
||||||
span(style="color:#909399;font-size:12px;margin-left:5px") {{ emojiTable.length }}/9
|
span(style="color:#909399;font-size:12px;margin-left:5px") {{ emojiTable.length }}/9
|
||||||
input(type="file" accept="image/*" @change="onFileChangeEmoji" id="EmojiUploadButton" style="display:none")
|
input(type="file" accept="image/png,image/jpg,image/jpeg,image/webp,image/bmp,image/gif" @change="onFileChangeEmoji" id="EmojiUploadButton" style="display:none")
|
||||||
el-button-group(style="margin-right:10px")
|
el-button-group(style="margin-right:10px")
|
||||||
el-button(type="default" size="small" @click="refreshEmojiTable" icon="el-icon-refresh") {{ $t('dialog.gallery_icons.refresh') }}
|
el-button(type="default" size="small" @click="refreshEmojiTable" icon="el-icon-refresh") {{ $t('dialog.gallery_icons.refresh') }}
|
||||||
el-button(type="default" size="small" @click="displayEmojiUpload" icon="el-icon-upload2" :disabled="!API.currentUser.$isVRCPlus") {{ $t('dialog.gallery_icons.upload') }}
|
el-button(type="default" size="small" @click="displayEmojiUpload" icon="el-icon-upload2" :disabled="!API.currentUser.$isVRCPlus") {{ $t('dialog.gallery_icons.upload') }}
|
||||||
@@ -2977,7 +2977,7 @@ html
|
|||||||
span(slot="label") {{ $t('dialog.gallery_select.gallery') }}
|
span(slot="label") {{ $t('dialog.gallery_select.gallery') }}
|
||||||
span(style="color:#909399;font-size:12px;margin-left:5px") {{ galleryTable.length }}/64
|
span(style="color:#909399;font-size:12px;margin-left:5px") {{ galleryTable.length }}/64
|
||||||
br
|
br
|
||||||
input(type="file" accept="image/*" @change="onFileChangeGallery" id="GalleryUploadButton" style="display:none")
|
input(type="file" accept="image/png,image/jpg,image/jpeg,image/webp,image/bmp,image/gif" @change="onFileChangeGallery" id="GalleryUploadButton" style="display:none")
|
||||||
el-button-group
|
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="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') }}
|
el-button(type="default" size="small" @click="refreshGalleryTable" icon="el-icon-refresh") {{ $t('dialog.gallery_select.refresh') }}
|
||||||
|
|||||||
Reference in New Issue
Block a user