mirror of
https://github.com/microsoft/PowerToys.git
synced 2024-12-20 06:47:56 +08:00
Merge remote-tracking branch 'origin/main' into dev/snickler/net8-upgrade
This commit is contained in:
commit
5867b12d67
2
.github/actions/spell-check/allow/names.txt
vendored
2
.github/actions/spell-check/allow/names.txt
vendored
@ -35,6 +35,7 @@ mshtang
|
|||||||
Myrvold
|
Myrvold
|
||||||
naveensrinivasan
|
naveensrinivasan
|
||||||
nVidia
|
nVidia
|
||||||
|
phoboslab
|
||||||
Ponten
|
Ponten
|
||||||
Pooja
|
Pooja
|
||||||
robmen
|
robmen
|
||||||
@ -44,6 +45,7 @@ skycommand
|
|||||||
snickler
|
snickler
|
||||||
sinclairinat
|
sinclairinat
|
||||||
streamjsonrpc
|
streamjsonrpc
|
||||||
|
Szablewski
|
||||||
tilovell
|
tilovell
|
||||||
TheJoeFin
|
TheJoeFin
|
||||||
Triet
|
Triet
|
||||||
|
3
.github/actions/spell-check/expect.txt
vendored
3
.github/actions/spell-check/expect.txt
vendored
@ -1014,6 +1014,7 @@ lstrlen
|
|||||||
LTRB
|
LTRB
|
||||||
LTRREADING
|
LTRREADING
|
||||||
luid
|
luid
|
||||||
|
LUMA
|
||||||
lusrmgr
|
lusrmgr
|
||||||
LVal
|
LVal
|
||||||
LWA
|
LWA
|
||||||
@ -1496,6 +1497,7 @@ qianlifeng
|
|||||||
qit
|
qit
|
||||||
QITAB
|
QITAB
|
||||||
QITABENT
|
QITABENT
|
||||||
|
qoi
|
||||||
qps
|
qps
|
||||||
Quarternary
|
Quarternary
|
||||||
QUERYENDSESSION
|
QUERYENDSESSION
|
||||||
@ -2250,6 +2252,7 @@ xsi
|
|||||||
XStr
|
XStr
|
||||||
XUP
|
XUP
|
||||||
XVIRTUALSCREEN
|
XVIRTUALSCREEN
|
||||||
|
xxxxxx
|
||||||
YAxis
|
YAxis
|
||||||
ycv
|
ycv
|
||||||
Yeet
|
Yeet
|
||||||
|
28
NOTICE.md
28
NOTICE.md
@ -67,6 +67,34 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
|
### The Quite OK Image Format reference decoder
|
||||||
|
|
||||||
|
**Source**: https://github.com/phoboslab/qoi
|
||||||
|
|
||||||
|
**Note**: [@pedrolamas](https://github.com/pedrolamas) translated and adapted the reference decoder code to C# that is in PowerToys from the original C++ implementation.
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 Dominic Szablewski
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
## Utility: ImageResizer
|
## Utility: ImageResizer
|
||||||
|
|
||||||
### Brice Lams's Image Resizer License
|
### Brice Lams's Image Resizer License
|
||||||
|
@ -104,7 +104,6 @@ IFACEMETHODIMP ExplorerCommand::Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataOb
|
|||||||
if (pdtobj)
|
if (pdtobj)
|
||||||
{
|
{
|
||||||
m_data_obj = pdtobj;
|
m_data_obj = pdtobj;
|
||||||
m_data_obj->AddRef();
|
|
||||||
}
|
}
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
@ -242,10 +241,6 @@ ExplorerCommand::ExplorerCommand()
|
|||||||
|
|
||||||
ExplorerCommand::~ExplorerCommand()
|
ExplorerCommand::~ExplorerCommand()
|
||||||
{
|
{
|
||||||
if (m_data_obj)
|
|
||||||
{
|
|
||||||
m_data_obj->Release();
|
|
||||||
}
|
|
||||||
--globals::ref_count;
|
--globals::ref_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +90,19 @@ namespace MouseWithoutBorders
|
|||||||
lock (ThreadsLock)
|
lock (ThreadsLock)
|
||||||
{
|
{
|
||||||
#pragma warning disable 618 // Temporary
|
#pragma warning disable 618 // Temporary
|
||||||
threads.Where(t => t.IsAlive && t.ManagedThreadId != threadId).ToList().ForEach(t => t.Suspend());
|
threads.Where(t => t.IsAlive && t.ManagedThreadId != threadId).ToList().ForEach(
|
||||||
|
t =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
t.Suspend();
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// This method is suspending every thread so that it can kill the process right after restarting.
|
||||||
|
// Makes no sense to crash on a thread suspension fail, since we're killing the process afterwards, anyway.
|
||||||
|
}
|
||||||
|
});
|
||||||
#pragma warning restore 618
|
#pragma warning restore 618
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,13 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto process = GetCurrentProcess();
|
||||||
|
if (!SetPriorityClass(process, NORMAL_PRIORITY_CLASS))
|
||||||
|
{
|
||||||
|
std::wstring err = get_last_error_or_default(GetLastError());
|
||||||
|
Logger::warn(L"Failed to set priority to FancyZones: {}", err);
|
||||||
|
}
|
||||||
|
|
||||||
std::wstring pid = std::wstring(lpCmdLine);
|
std::wstring pid = std::wstring(lpCmdLine);
|
||||||
if (!pid.empty())
|
if (!pid.empty())
|
||||||
{
|
{
|
||||||
|
@ -71,6 +71,11 @@ public:
|
|||||||
PostMessageW(m_window, WM_PRIV_LOCATIONCHANGE, NULL, NULL);
|
PostMessageW(m_window, WM_PRIV_LOCATIONCHANGE, NULL, NULL);
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
|
if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL))
|
||||||
|
{
|
||||||
|
Logger::warn("Failed to set main thread priority");
|
||||||
|
}
|
||||||
|
|
||||||
this->disableModuleCallback = std::move(disableModuleCallbackFunction);
|
this->disableModuleCallback = std::move(disableModuleCallbackFunction);
|
||||||
|
|
||||||
FancyZonesSettings::instance().LoadSettings();
|
FancyZonesSettings::instance().LoadSettings();
|
||||||
|
@ -171,6 +171,11 @@ namespace PowerLauncher
|
|||||||
_settings.GenerateThumbnailsFromFiles = overloadSettings.Properties.GenerateThumbnailsFromFiles;
|
_settings.GenerateThumbnailsFromFiles = overloadSettings.Properties.GenerateThumbnailsFromFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_settings.ShouldUsePinyin != overloadSettings.Properties.UsePinyin)
|
||||||
|
{
|
||||||
|
_settings.ShouldUsePinyin = overloadSettings.Properties.UsePinyin;
|
||||||
|
}
|
||||||
|
|
||||||
retry = false;
|
retry = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,14 +2,6 @@
|
|||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.InteropServices.ComTypes;
|
|
||||||
using System.Text;
|
|
||||||
using System.Windows.Forms;
|
|
||||||
using Common;
|
using Common;
|
||||||
using Common.Utilities;
|
using Common.Utilities;
|
||||||
using Microsoft.PowerToys.PreviewHandler.Gcode.Telemetry.Events;
|
using Microsoft.PowerToys.PreviewHandler.Gcode.Telemetry.Events;
|
||||||
@ -67,7 +59,9 @@ namespace Microsoft.PowerToys.PreviewHandler.Gcode
|
|||||||
|
|
||||||
using (var reader = new StreamReader(fs))
|
using (var reader = new StreamReader(fs))
|
||||||
{
|
{
|
||||||
thumbnail = GetThumbnail(reader);
|
var gcodeThumbnail = GcodeHelper.GetBestThumbnail(reader);
|
||||||
|
|
||||||
|
thumbnail = gcodeThumbnail?.GetBitmap();
|
||||||
}
|
}
|
||||||
|
|
||||||
_infoBarAdded = false;
|
_infoBarAdded = false;
|
||||||
@ -84,7 +78,13 @@ namespace Microsoft.PowerToys.PreviewHandler.Gcode
|
|||||||
|
|
||||||
Resize += FormResized;
|
Resize += FormResized;
|
||||||
base.DoPreview(fs);
|
base.DoPreview(fs);
|
||||||
PowerToysTelemetry.Log.WriteEvent(new GcodeFilePreviewed());
|
try
|
||||||
|
{
|
||||||
|
PowerToysTelemetry.Log.WriteEvent(new GcodeFilePreviewed());
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{ // Should not crash if sending telemetry is failing. Ignore the exception.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -92,66 +92,6 @@ namespace Microsoft.PowerToys.PreviewHandler.Gcode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reads the G-code content searching for thumbnails and returns the largest.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="reader">The TextReader instance for the G-code content.</param>
|
|
||||||
/// <returns>A thumbnail extracted from the G-code content.</returns>
|
|
||||||
public static Bitmap GetThumbnail(TextReader reader)
|
|
||||||
{
|
|
||||||
if (reader == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Bitmap thumbnail = null;
|
|
||||||
|
|
||||||
var bitmapBase64 = GetBase64Thumbnails(reader)
|
|
||||||
.OrderByDescending(x => x.Length)
|
|
||||||
.FirstOrDefault();
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(bitmapBase64))
|
|
||||||
{
|
|
||||||
var bitmapBytes = Convert.FromBase64String(bitmapBase64);
|
|
||||||
|
|
||||||
thumbnail = new Bitmap(new MemoryStream(bitmapBytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
return thumbnail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets all thumbnails in base64 format found on the G-code data.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="reader">The TextReader instance for the G-code content.</param>
|
|
||||||
/// <returns>An enumeration of thumbnails in base64 format found on the G-code.</returns>
|
|
||||||
private static IEnumerable<string> GetBase64Thumbnails(TextReader reader)
|
|
||||||
{
|
|
||||||
string line;
|
|
||||||
StringBuilder capturedText = null;
|
|
||||||
|
|
||||||
while ((line = reader.ReadLine()) != null)
|
|
||||||
{
|
|
||||||
if (line.StartsWith("; thumbnail begin", StringComparison.InvariantCulture))
|
|
||||||
{
|
|
||||||
capturedText = new StringBuilder();
|
|
||||||
}
|
|
||||||
else if (line == "; thumbnail end")
|
|
||||||
{
|
|
||||||
if (capturedText != null)
|
|
||||||
{
|
|
||||||
yield return capturedText.ToString();
|
|
||||||
|
|
||||||
capturedText = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (capturedText != null)
|
|
||||||
{
|
|
||||||
capturedText.Append(line[2..]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when RichtextBox is resized.
|
/// Occurs when RichtextBox is resized.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -214,7 +154,14 @@ namespace Microsoft.PowerToys.PreviewHandler.Gcode
|
|||||||
/// <param name="dataSource">Stream reference to access source file.</param>
|
/// <param name="dataSource">Stream reference to access source file.</param>
|
||||||
private void PreviewError<T>(Exception exception, T dataSource)
|
private void PreviewError<T>(Exception exception, T dataSource)
|
||||||
{
|
{
|
||||||
PowerToysTelemetry.Log.WriteEvent(new GcodeFilePreviewError { Message = exception.Message });
|
try
|
||||||
|
{
|
||||||
|
PowerToysTelemetry.Log.WriteEvent(new GcodeFilePreviewError { Message = exception.Message });
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{ // Should not crash if sending telemetry is failing. Ignore the exception.
|
||||||
|
}
|
||||||
|
|
||||||
Controls.Clear();
|
Controls.Clear();
|
||||||
_infoBarAdded = true;
|
_infoBarAdded = true;
|
||||||
AddTextBoxControl(Properties.Resource.GcodeNotPreviewedError);
|
AddTextBoxControl(Properties.Resource.GcodeNotPreviewedError);
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
using System.Drawing.Drawing2D;
|
using System.Drawing.Drawing2D;
|
||||||
using System.Text;
|
using Common.Utilities;
|
||||||
|
|
||||||
namespace Microsoft.PowerToys.ThumbnailHandler.Gcode
|
namespace Microsoft.PowerToys.ThumbnailHandler.Gcode
|
||||||
{
|
{
|
||||||
@ -45,64 +45,23 @@ namespace Microsoft.PowerToys.ThumbnailHandler.Gcode
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bitmap thumbnail = null;
|
var gcodeThumbnail = GcodeHelper.GetBestThumbnail(reader);
|
||||||
|
|
||||||
var bitmapBase64 = GetBase64Thumbnails(reader)
|
var thumbnail = gcodeThumbnail?.GetBitmap();
|
||||||
.OrderByDescending(x => x.Length)
|
|
||||||
.FirstOrDefault();
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(bitmapBase64))
|
if (thumbnail != null && thumbnail.Width != cx && thumbnail.Height != cx)
|
||||||
{
|
{
|
||||||
var bitmapBytes = Convert.FromBase64String(bitmapBase64);
|
// We are not the appropriate size for caller. Resize now while
|
||||||
|
// respecting the aspect ratio.
|
||||||
thumbnail = new Bitmap(new MemoryStream(bitmapBytes));
|
float scale = Math.Min((float)cx / thumbnail.Width, (float)cx / thumbnail.Height);
|
||||||
|
int scaleWidth = (int)(thumbnail.Width * scale);
|
||||||
if (thumbnail.Width != cx && thumbnail.Height != cx)
|
int scaleHeight = (int)(thumbnail.Height * scale);
|
||||||
{
|
thumbnail = ResizeImage(thumbnail, scaleWidth, scaleHeight);
|
||||||
// We are not the appropriate size for caller. Resize now while
|
|
||||||
// respecting the aspect ratio.
|
|
||||||
float scale = Math.Min((float)cx / thumbnail.Width, (float)cx / thumbnail.Height);
|
|
||||||
int scaleWidth = (int)(thumbnail.Width * scale);
|
|
||||||
int scaleHeight = (int)(thumbnail.Height * scale);
|
|
||||||
thumbnail = ResizeImage(thumbnail, scaleWidth, scaleHeight);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return thumbnail;
|
return thumbnail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets all thumbnails in base64 format found on the G-code data.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="reader">The TextReader instance for the G-code content.</param>
|
|
||||||
/// <returns>An enumeration of thumbnails in base64 format found on the G-code.</returns>
|
|
||||||
private static IEnumerable<string> GetBase64Thumbnails(TextReader reader)
|
|
||||||
{
|
|
||||||
string line;
|
|
||||||
StringBuilder capturedText = null;
|
|
||||||
|
|
||||||
while ((line = reader.ReadLine()) != null)
|
|
||||||
{
|
|
||||||
if (line.StartsWith("; thumbnail begin", StringComparison.InvariantCulture))
|
|
||||||
{
|
|
||||||
capturedText = new StringBuilder();
|
|
||||||
}
|
|
||||||
else if (line == "; thumbnail end")
|
|
||||||
{
|
|
||||||
if (capturedText != null)
|
|
||||||
{
|
|
||||||
yield return capturedText.ToString();
|
|
||||||
|
|
||||||
capturedText = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (capturedText != null)
|
|
||||||
{
|
|
||||||
capturedText.Append(line[2..]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resize the image with high quality to the specified width and height.
|
/// Resize the image with high quality to the specified width and height.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -200,11 +200,23 @@ namespace Microsoft.PowerToys.PreviewHandler.Markdown
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
PowerToysTelemetry.Log.WriteEvent(new MarkdownFilePreviewed());
|
try
|
||||||
|
{
|
||||||
|
PowerToysTelemetry.Log.WriteEvent(new MarkdownFilePreviewed());
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{ // Should not crash if sending telemetry is failing. Ignore the exception.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
PowerToysTelemetry.Log.WriteEvent(new MarkdownFilePreviewError { Message = ex.Message });
|
try
|
||||||
|
{
|
||||||
|
PowerToysTelemetry.Log.WriteEvent(new MarkdownFilePreviewError { Message = ex.Message });
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{ // Should not crash if sending telemetry is failing. Ignore the exception.
|
||||||
|
}
|
||||||
|
|
||||||
Controls.Clear();
|
Controls.Clear();
|
||||||
_infoBarDisplayed = true;
|
_infoBarDisplayed = true;
|
||||||
|
@ -161,11 +161,23 @@ namespace Microsoft.PowerToys.PreviewHandler.Pdf
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PowerToysTelemetry.Log.WriteEvent(new PdfFilePreviewed());
|
try
|
||||||
|
{
|
||||||
|
PowerToysTelemetry.Log.WriteEvent(new PdfFilePreviewed());
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{ // Should not crash if sending telemetry is failing. Ignore the exception.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
PowerToysTelemetry.Log.WriteEvent(new PdfFilePreviewError { Message = ex.Message });
|
try
|
||||||
|
{
|
||||||
|
PowerToysTelemetry.Log.WriteEvent(new PdfFilePreviewError { Message = ex.Message });
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{ // Should not crash if sending telemetry is failing. Ignore the exception.
|
||||||
|
}
|
||||||
|
|
||||||
Controls.Clear();
|
Controls.Clear();
|
||||||
_infoBar = GetTextBoxControl(Resources.PdfNotPreviewedError);
|
_infoBar = GetTextBoxControl(Resources.PdfNotPreviewedError);
|
||||||
|
@ -143,7 +143,13 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
PowerToysTelemetry.Log.WriteEvent(new SvgFilePreviewError { Message = ex.Message });
|
try
|
||||||
|
{
|
||||||
|
PowerToysTelemetry.Log.WriteEvent(new SvgFilePreviewError { Message = ex.Message });
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{ // Should not crash if sending telemetry is failing. Ignore the exception.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
@ -160,7 +166,13 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg
|
|||||||
AddWebViewControl(svgData);
|
AddWebViewControl(svgData);
|
||||||
Resize += FormResized;
|
Resize += FormResized;
|
||||||
base.DoPreview(dataSource);
|
base.DoPreview(dataSource);
|
||||||
PowerToysTelemetry.Log.WriteEvent(new SvgFilePreviewed());
|
try
|
||||||
|
{
|
||||||
|
PowerToysTelemetry.Log.WriteEvent(new SvgFilePreviewed());
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{ // Should not crash if sending telemetry is failing. Ignore the exception.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -288,7 +300,14 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg
|
|||||||
/// <param name="dataSource">Stream reference to access source file.</param>
|
/// <param name="dataSource">Stream reference to access source file.</param>
|
||||||
private void PreviewError<T>(Exception exception, T dataSource)
|
private void PreviewError<T>(Exception exception, T dataSource)
|
||||||
{
|
{
|
||||||
PowerToysTelemetry.Log.WriteEvent(new SvgFilePreviewError { Message = exception.Message });
|
try
|
||||||
|
{
|
||||||
|
PowerToysTelemetry.Log.WriteEvent(new SvgFilePreviewError { Message = exception.Message });
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{ // Should not crash if sending telemetry is failing. Ignore the exception.
|
||||||
|
}
|
||||||
|
|
||||||
Controls.Clear();
|
Controls.Clear();
|
||||||
_infoBarAdded = true;
|
_infoBarAdded = true;
|
||||||
AddTextBoxControl(Properties.Resource.SvgNotPreviewedError);
|
AddTextBoxControl(Properties.Resource.SvgNotPreviewedError);
|
||||||
|
@ -19,14 +19,17 @@ namespace GcodePreviewHandlerUnitTests
|
|||||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "new Exception() is fine in test projects.")]
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "new Exception() is fine in test projects.")]
|
||||||
public class GcodePreviewHandlerTest
|
public class GcodePreviewHandlerTest
|
||||||
{
|
{
|
||||||
[TestMethod]
|
[DataTestMethod]
|
||||||
public void GcodePreviewHandlerControlAddsControlsToFormWhenDoPreviewIsCalled()
|
[DataRow("HelperFiles/sample.gcode")]
|
||||||
|
[DataRow("HelperFiles/sample_JPG.gcode")]
|
||||||
|
[DataRow("HelperFiles/sample_QOI.gcode")]
|
||||||
|
public void GcodePreviewHandlerControlAddsControlsToFormWhenDoPreviewIsCalled(string filePath)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
using (var gcodePreviewHandlerControl = new GcodePreviewHandlerControl())
|
using (var gcodePreviewHandlerControl = new GcodePreviewHandlerControl())
|
||||||
{
|
{
|
||||||
// Act
|
// Act
|
||||||
var file = File.ReadAllBytes("HelperFiles/sample.gcode");
|
var file = File.ReadAllBytes(filePath);
|
||||||
|
|
||||||
gcodePreviewHandlerControl.DoPreview<IStream>(GetMockStream(file));
|
gcodePreviewHandlerControl.DoPreview<IStream>(GetMockStream(file));
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -22,6 +22,8 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Remove="HelperFiles\sample.gcode" />
|
<None Remove="HelperFiles\sample.gcode" />
|
||||||
|
<None Remove="HelperFiles\sample_JPG.gcode" />
|
||||||
|
<None Remove="HelperFiles\sample_QOI.gcode" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -41,6 +43,12 @@
|
|||||||
<Content Include="HelperFiles\sample.gcode">
|
<Content Include="HelperFiles\sample.gcode">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Include="HelperFiles\sample_JPG.gcode">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="HelperFiles\sample_QOI.gcode">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
<Compile Include="..\STATestClassAttribute.cs" Link="STATestClassAttribute.cs" />
|
<Compile Include="..\STATestClassAttribute.cs" Link="STATestClassAttribute.cs" />
|
||||||
<Compile Include="..\STATestMethodAttribute.cs" Link="STATestMethodAttribute.cs" />
|
<Compile Include="..\STATestMethodAttribute.cs" Link="STATestMethodAttribute.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -2,28 +2,24 @@
|
|||||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Runtime.InteropServices.ComTypes;
|
|
||||||
using Common.ComInterlop;
|
|
||||||
using Microsoft.PowerToys.STATestExtension;
|
using Microsoft.PowerToys.STATestExtension;
|
||||||
using Microsoft.PowerToys.ThumbnailHandler.Gcode;
|
using Microsoft.PowerToys.ThumbnailHandler.Gcode;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using Moq;
|
|
||||||
|
|
||||||
namespace GcodeThumbnailProviderUnitTests
|
namespace GcodeThumbnailProviderUnitTests
|
||||||
{
|
{
|
||||||
[STATestClass]
|
[STATestClass]
|
||||||
public class GcodeThumbnailProviderTests
|
public class GcodeThumbnailProviderTests
|
||||||
{
|
{
|
||||||
[TestMethod]
|
[DataTestMethod]
|
||||||
public void GetThumbnailValidStreamGcode()
|
[DataRow("HelperFiles/sample.gcode")]
|
||||||
|
[DataRow("HelperFiles/sample_JPG.gcode")]
|
||||||
|
[DataRow("HelperFiles/sample_QOI.gcode")]
|
||||||
|
public void GetThumbnailValidStreamGcode(string filePath)
|
||||||
{
|
{
|
||||||
// Act
|
// Act
|
||||||
var filePath = "HelperFiles/sample.gcode";
|
|
||||||
|
|
||||||
GcodeThumbnailProvider provider = new GcodeThumbnailProvider(filePath);
|
GcodeThumbnailProvider provider = new GcodeThumbnailProvider(filePath);
|
||||||
|
|
||||||
Bitmap bitmap = provider.GetThumbnail(256);
|
Bitmap bitmap = provider.GetThumbnail(256);
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -23,6 +23,8 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Remove="HelperFiles\sample.gcode" />
|
<None Remove="HelperFiles\sample.gcode" />
|
||||||
|
<None Remove="HelperFiles\sample_JPG.gcode" />
|
||||||
|
<None Remove="HelperFiles\sample_QOI.gcode" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -44,5 +46,11 @@
|
|||||||
<Content Include="HelperFiles\sample.gcode">
|
<Content Include="HelperFiles\sample.gcode">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
</Content>
|
</Content>
|
||||||
|
<Content Include="HelperFiles\sample_JPG.gcode">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Include="HelperFiles\sample_QOI.gcode">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<UseWindowsForms>true</UseWindowsForms>
|
<UseWindowsForms>true</UseWindowsForms>
|
||||||
<AssemblyTitle>PowerToys.PreviewHandlerCommon</AssemblyTitle>
|
<AssemblyTitle>PowerToys.PreviewHandlerCommon</AssemblyTitle>
|
||||||
@ -9,6 +9,8 @@
|
|||||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
81
src/modules/previewpane/common/Utilities/GcodeHelper.cs
Normal file
81
src/modules/previewpane/common/Utilities/GcodeHelper.cs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Common.Utilities
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gcode file helper class.
|
||||||
|
/// </summary>
|
||||||
|
public static class GcodeHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets any thumbnails found in a gcode file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">The <see cref="TextReader"/> instance to the gcode file.</param>
|
||||||
|
/// <returns>The thumbnails found in a gcode file.</returns>
|
||||||
|
public static IEnumerable<GcodeThumbnail> GetThumbnails(TextReader reader)
|
||||||
|
{
|
||||||
|
string? line;
|
||||||
|
var format = GcodeThumbnailFormat.Unknown;
|
||||||
|
StringBuilder? capturedText = null;
|
||||||
|
|
||||||
|
while ((line = reader.ReadLine()) != null)
|
||||||
|
{
|
||||||
|
if (line.StartsWith("; thumbnail", StringComparison.InvariantCulture))
|
||||||
|
{
|
||||||
|
var parts = line[11..].Split(" ");
|
||||||
|
|
||||||
|
switch (parts[1])
|
||||||
|
{
|
||||||
|
case "begin":
|
||||||
|
format = parts[0].ToUpperInvariant() switch
|
||||||
|
{
|
||||||
|
"" => GcodeThumbnailFormat.PNG,
|
||||||
|
"_JPG" => GcodeThumbnailFormat.JPG,
|
||||||
|
"_QOI" => GcodeThumbnailFormat.QOI,
|
||||||
|
_ => GcodeThumbnailFormat.Unknown,
|
||||||
|
};
|
||||||
|
capturedText = new StringBuilder();
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "end":
|
||||||
|
if (capturedText != null)
|
||||||
|
{
|
||||||
|
yield return new GcodeThumbnail(format, capturedText.ToString());
|
||||||
|
|
||||||
|
capturedText = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
capturedText?.Append(line[2..]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the best thumbnail available in a gcode file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reader">The <see cref="TextReader"/> instance to the gcode file.</param>
|
||||||
|
/// <returns>The best thumbnail available in the gcode file.</returns>
|
||||||
|
public static GcodeThumbnail? GetBestThumbnail(TextReader reader)
|
||||||
|
{
|
||||||
|
return GetThumbnails(reader)
|
||||||
|
.Where(x => x.Format != GcodeThumbnailFormat.Unknown)
|
||||||
|
.OrderByDescending(x => (int)x.Format)
|
||||||
|
.ThenByDescending(x => x.Data.Length)
|
||||||
|
.FirstOrDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
72
src/modules/previewpane/common/Utilities/GcodeThumbnail.cs
Normal file
72
src/modules/previewpane/common/Utilities/GcodeThumbnail.cs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
|
using PreviewHandlerCommon.Utilities;
|
||||||
|
|
||||||
|
namespace Common.Utilities
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a gcode thumbnail.
|
||||||
|
/// </summary>
|
||||||
|
public class GcodeThumbnail
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the gcode thumbnail image format.
|
||||||
|
/// </summary>
|
||||||
|
public GcodeThumbnailFormat Format { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the gcode thumbnail image data in base64.
|
||||||
|
/// </summary>
|
||||||
|
public string Data { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="GcodeThumbnail"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format">The gcode thumbnail image format.</param>
|
||||||
|
/// <param name="data">The gcode thumbnail image data in base64.</param>
|
||||||
|
public GcodeThumbnail(GcodeThumbnailFormat format, string data)
|
||||||
|
{
|
||||||
|
Format = format;
|
||||||
|
Data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a <see cref="Bitmap"/> representing this thumbnail.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A <see cref="Bitmap"/> representing this thumbnail.</returns>
|
||||||
|
public Bitmap? GetBitmap()
|
||||||
|
{
|
||||||
|
switch (Format)
|
||||||
|
{
|
||||||
|
case GcodeThumbnailFormat.JPG:
|
||||||
|
case GcodeThumbnailFormat.PNG:
|
||||||
|
return BitmapFromBase64String();
|
||||||
|
|
||||||
|
case GcodeThumbnailFormat.QOI:
|
||||||
|
return BitmapFromQoiBase64String();
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bitmap BitmapFromBase64String()
|
||||||
|
{
|
||||||
|
var bitmapBytes = Convert.FromBase64String(Data);
|
||||||
|
|
||||||
|
return new Bitmap(new MemoryStream(bitmapBytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bitmap BitmapFromQoiBase64String()
|
||||||
|
{
|
||||||
|
var bitmapBytes = Convert.FromBase64String(Data);
|
||||||
|
|
||||||
|
return QoiImage.FromStream(new MemoryStream(bitmapBytes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
namespace Common.Utilities
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The gcode thumbnail image format.
|
||||||
|
/// </summary>
|
||||||
|
public enum GcodeThumbnailFormat
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Unknown image format.
|
||||||
|
/// </summary>
|
||||||
|
Unknown,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// JPG image format.
|
||||||
|
/// </summary>
|
||||||
|
JPG,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// QOI image format.
|
||||||
|
/// </summary>
|
||||||
|
QOI,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// PNG image format.
|
||||||
|
/// </summary>
|
||||||
|
PNG,
|
||||||
|
}
|
||||||
|
}
|
177
src/modules/previewpane/common/Utilities/QoiImage.cs
Normal file
177
src/modules/previewpane/common/Utilities/QoiImage.cs
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation
|
||||||
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||||
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Buffers.Binary;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Drawing.Imaging;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
//// Based on https://github.com/phoboslab/qoi/blob/master/qoi.h
|
||||||
|
|
||||||
|
namespace PreviewHandlerCommon.Utilities
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// QOI Image helper.
|
||||||
|
/// </summary>
|
||||||
|
public static class QoiImage
|
||||||
|
{
|
||||||
|
#pragma warning disable SA1310 // Field names should not contain underscore
|
||||||
|
private const byte QOI_OP_INDEX = 0x00; // 00xxxxxx
|
||||||
|
private const byte QOI_OP_DIFF = 0x40; // 01xxxxxx
|
||||||
|
private const byte QOI_OP_LUMA = 0x80; // 10xxxxxx
|
||||||
|
private const byte QOI_OP_RUN = 0xc0; // 11xxxxxx
|
||||||
|
private const byte QOI_OP_RGB = 0xfe; // 11111110
|
||||||
|
private const byte QOI_OP_RGBA = 0xff; // 11111111
|
||||||
|
|
||||||
|
private const byte QOI_MASK_2 = 0xc0; // 11000000
|
||||||
|
|
||||||
|
private const int QOI_MAGIC = 'q' << 24 | 'o' << 16 | 'i' << 8 | 'f';
|
||||||
|
private const int QOI_HEADER_SIZE = 14;
|
||||||
|
|
||||||
|
private const uint QOI_PIXELS_MAX = 400000000;
|
||||||
|
|
||||||
|
private const byte QOI_PADDING_LENGTH = 8;
|
||||||
|
#pragma warning restore SA1310 // Field names should not contain underscore
|
||||||
|
|
||||||
|
private record struct QoiPixel(byte R, byte G, byte B, byte A)
|
||||||
|
{
|
||||||
|
public readonly int GetColorHash() => (R * 3) + (G * 5) + (B * 7) + (A * 11);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="Bitmap"/> from the specified QOI data stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">A <see cref="Stream"/> that contains the QOI data.</param>
|
||||||
|
/// <returns>The <see cref="Bitmap"/> this method creates.</returns>
|
||||||
|
/// <exception cref="ArgumentException">The stream does not have a valid QOI image format.</exception>
|
||||||
|
public static Bitmap FromStream(Stream stream)
|
||||||
|
{
|
||||||
|
var fileSize = stream.Length;
|
||||||
|
|
||||||
|
if (fileSize < QOI_HEADER_SIZE + QOI_PADDING_LENGTH)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Not enough data for a QOI file");
|
||||||
|
}
|
||||||
|
|
||||||
|
using var reader = new BinaryReader(stream);
|
||||||
|
|
||||||
|
var headerMagic = ReadUInt32BigEndian(reader);
|
||||||
|
|
||||||
|
if (headerMagic != QOI_MAGIC)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Invalid QOI file header");
|
||||||
|
}
|
||||||
|
|
||||||
|
var width = ReadUInt32BigEndian(reader);
|
||||||
|
var height = ReadUInt32BigEndian(reader);
|
||||||
|
var channels = reader.ReadByte();
|
||||||
|
var colorSpace = reader.ReadByte();
|
||||||
|
|
||||||
|
if (width == 0 || height == 0 || channels < 3 || channels > 4 || colorSpace > 1 || height >= QOI_PIXELS_MAX / width)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Invalid QOI file data");
|
||||||
|
}
|
||||||
|
|
||||||
|
var pixelsCount = width * height;
|
||||||
|
var pixels = new QoiPixel[pixelsCount];
|
||||||
|
var index = new QoiPixel[64];
|
||||||
|
|
||||||
|
var pixel = new QoiPixel(0, 0, 0, 255);
|
||||||
|
|
||||||
|
var run = 0;
|
||||||
|
var chunksLen = fileSize - QOI_PADDING_LENGTH;
|
||||||
|
|
||||||
|
for (var pixelIndex = 0; pixelIndex < pixelsCount; pixelIndex++)
|
||||||
|
{
|
||||||
|
if (run > 0)
|
||||||
|
{
|
||||||
|
run--;
|
||||||
|
}
|
||||||
|
else if (stream.Position < chunksLen)
|
||||||
|
{
|
||||||
|
var b1 = reader.ReadByte();
|
||||||
|
|
||||||
|
if (b1 == QOI_OP_RGB)
|
||||||
|
{
|
||||||
|
pixel.R = reader.ReadByte();
|
||||||
|
pixel.G = reader.ReadByte();
|
||||||
|
pixel.B = reader.ReadByte();
|
||||||
|
}
|
||||||
|
else if (b1 == QOI_OP_RGBA)
|
||||||
|
{
|
||||||
|
pixel.R = reader.ReadByte();
|
||||||
|
pixel.G = reader.ReadByte();
|
||||||
|
pixel.B = reader.ReadByte();
|
||||||
|
pixel.A = reader.ReadByte();
|
||||||
|
}
|
||||||
|
else if ((b1 & QOI_MASK_2) == QOI_OP_INDEX)
|
||||||
|
{
|
||||||
|
pixel = index[b1];
|
||||||
|
}
|
||||||
|
else if ((b1 & QOI_MASK_2) == QOI_OP_DIFF)
|
||||||
|
{
|
||||||
|
pixel.R += (byte)(((b1 >> 4) & 0x03) - 2);
|
||||||
|
pixel.G += (byte)(((b1 >> 2) & 0x03) - 2);
|
||||||
|
pixel.B += (byte)((b1 & 0x03) - 2);
|
||||||
|
}
|
||||||
|
else if ((b1 & QOI_MASK_2) == QOI_OP_LUMA)
|
||||||
|
{
|
||||||
|
var b2 = reader.ReadByte();
|
||||||
|
var vg = (b1 & 0x3f) - 32;
|
||||||
|
pixel.R += (byte)(vg - 8 + ((b2 >> 4) & 0x0f));
|
||||||
|
pixel.G += (byte)vg;
|
||||||
|
pixel.B += (byte)(vg - 8 + (b2 & 0x0f));
|
||||||
|
}
|
||||||
|
else if ((b1 & QOI_MASK_2) == QOI_OP_RUN)
|
||||||
|
{
|
||||||
|
run = b1 & 0x3f;
|
||||||
|
}
|
||||||
|
|
||||||
|
index[pixel.GetColorHash() % 64] = pixel;
|
||||||
|
}
|
||||||
|
|
||||||
|
pixels[pixelIndex] = pixel;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ConvertToBitmap(width, height, channels, pixels);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Bitmap ConvertToBitmap(uint width, uint height, byte channels, QoiPixel[] pixels)
|
||||||
|
{
|
||||||
|
var pixelFormat = channels == 4 ? PixelFormat.Format32bppArgb : PixelFormat.Format24bppRgb;
|
||||||
|
var bitmap = new Bitmap((int)width, (int)height, pixelFormat);
|
||||||
|
|
||||||
|
var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.WriteOnly, pixelFormat);
|
||||||
|
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
for (var pixelIndex = 0; pixelIndex < pixels.Length; pixelIndex++)
|
||||||
|
{
|
||||||
|
var pixel = pixels[pixelIndex];
|
||||||
|
var bitmapPixel = (byte*)bitmapData.Scan0 + (pixelIndex * channels);
|
||||||
|
|
||||||
|
bitmapPixel[0] = pixel.B;
|
||||||
|
bitmapPixel[1] = pixel.G;
|
||||||
|
bitmapPixel[2] = pixel.R;
|
||||||
|
if (channels == 4)
|
||||||
|
{
|
||||||
|
bitmapPixel[3] = pixel.A;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitmap.UnlockBits(bitmapData);
|
||||||
|
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint ReadUInt32BigEndian(BinaryReader reader)
|
||||||
|
{
|
||||||
|
var buffer = reader.ReadBytes(4);
|
||||||
|
|
||||||
|
return BinaryPrimitives.ReadUInt32BigEndian(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.InteropServices.ComTypes;
|
using System.Runtime.InteropServices.ComTypes;
|
||||||
@ -17,7 +18,7 @@ namespace Common.Utilities
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
public class ReadonlyStream : Stream
|
public class ReadonlyStream : Stream
|
||||||
{
|
{
|
||||||
private IStream _stream;
|
private IStream? _stream;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ReadonlyStream"/> class.
|
/// Initializes a new instance of the <see cref="ReadonlyStream"/> class.
|
||||||
@ -238,6 +239,7 @@ namespace Common.Utilities
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(_stream))]
|
||||||
private void CheckDisposed()
|
private void CheckDisposed()
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_stream == null, this);
|
ObjectDisposedException.ThrowIf(_stream == null, this);
|
||||||
|
@ -20,12 +20,12 @@ namespace Common
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// WebView2 Control to display Svg.
|
/// WebView2 Control to display Svg.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private WebView2 _browser;
|
private WebView2? _browser;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// WebView2 Environment
|
/// WebView2 Environment
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private CoreWebView2Environment _webView2Environment;
|
private CoreWebView2Environment? _webView2Environment;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Name of the virtual host
|
/// Name of the virtual host
|
||||||
@ -38,7 +38,7 @@ namespace Common
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Source: https://stackoverflow.com/a/283917/14774889
|
/// Source: https://stackoverflow.com/a/283917/14774889
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public static string AssemblyDirectory
|
public static string? AssemblyDirectory
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@ -60,7 +60,7 @@ namespace Common
|
|||||||
_browser = new WebView2();
|
_browser = new WebView2();
|
||||||
_browser.Dock = DockStyle.Fill;
|
_browser.Dock = DockStyle.Fill;
|
||||||
_browser.Visible = true;
|
_browser.Visible = true;
|
||||||
_browser.NavigationCompleted += (object sender, CoreWebView2NavigationCompletedEventArgs args) =>
|
_browser.NavigationCompleted += (object? sender, CoreWebView2NavigationCompletedEventArgs args) =>
|
||||||
{
|
{
|
||||||
// Put here logic needed after WebView2 control is done navigating to url/page
|
// Put here logic needed after WebView2 control is done navigating to url/page
|
||||||
};
|
};
|
||||||
@ -83,7 +83,7 @@ namespace Common
|
|||||||
_browser.NavigateToString("Test");
|
_browser.NavigateToString("Test");
|
||||||
|
|
||||||
// Or navigate to Uri
|
// Or navigate to Uri
|
||||||
_browser.Source = new Uri(filePath);
|
_browser.Source = new Uri(filePath!);
|
||||||
}
|
}
|
||||||
catch (NullReferenceException)
|
catch (NullReferenceException)
|
||||||
{
|
{
|
||||||
|
@ -72,6 +72,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
|||||||
[JsonPropertyName("search_wait_for_slow_results")]
|
[JsonPropertyName("search_wait_for_slow_results")]
|
||||||
public bool SearchWaitForSlowResults { get; set; }
|
public bool SearchWaitForSlowResults { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("use_pinyin")]
|
||||||
|
public bool UsePinyin { get; set; }
|
||||||
|
|
||||||
[JsonPropertyName("generate_thumbnails_from_files")]
|
[JsonPropertyName("generate_thumbnails_from_files")]
|
||||||
public bool GenerateThumbnailsFromFiles { get; set; }
|
public bool GenerateThumbnailsFromFiles { get; set; }
|
||||||
|
|
||||||
@ -103,6 +106,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
|||||||
SearchQueryTuningEnabled = false;
|
SearchQueryTuningEnabled = false;
|
||||||
SearchWaitForSlowResults = false;
|
SearchWaitForSlowResults = false;
|
||||||
GenerateThumbnailsFromFiles = true;
|
GenerateThumbnailsFromFiles = true;
|
||||||
|
UsePinyin = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,6 +81,7 @@ namespace ViewModelTests
|
|||||||
Assert.AreEqual(originalSettings.Properties.SearchResultPreference, viewModel.SearchResultPreference);
|
Assert.AreEqual(originalSettings.Properties.SearchResultPreference, viewModel.SearchResultPreference);
|
||||||
Assert.AreEqual(originalSettings.Properties.SearchTypePreference, viewModel.SearchTypePreference);
|
Assert.AreEqual(originalSettings.Properties.SearchTypePreference, viewModel.SearchTypePreference);
|
||||||
Assert.AreEqual(originalSettings.Properties.GenerateThumbnailsFromFiles, viewModel.GenerateThumbnailsFromFiles);
|
Assert.AreEqual(originalSettings.Properties.GenerateThumbnailsFromFiles, viewModel.GenerateThumbnailsFromFiles);
|
||||||
|
Assert.AreEqual(originalSettings.Properties.UsePinyin, viewModel.UsePinyin);
|
||||||
|
|
||||||
// Verify that the stub file was used
|
// Verify that the stub file was used
|
||||||
var expectedCallCount = 2; // once via the view model, and once by the test (GetSettings<T>)
|
var expectedCallCount = 2; // once via the view model, and once by the test (GetSettings<T>)
|
||||||
|
@ -153,6 +153,14 @@
|
|||||||
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.TabSelectsContextButtons, Mode=TwoWay}" />
|
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.TabSelectsContextButtons, Mode=TwoWay}" />
|
||||||
</controls:SettingsCard>
|
</controls:SettingsCard>
|
||||||
|
|
||||||
|
<controls:SettingsCard
|
||||||
|
x:Uid="PowerLauncher_UsePinyin"
|
||||||
|
HeaderIcon="{ui:FontIcon FontFamily={StaticResource SymbolThemeFontFamily}, Glyph=}">
|
||||||
|
<ToggleSwitch
|
||||||
|
x:Uid="ToggleSwitch"
|
||||||
|
IsOn="{x:Bind ViewModel.UsePinyin, Mode=TwoWay}" />
|
||||||
|
</controls:SettingsCard>
|
||||||
|
|
||||||
<controls:SettingsCard x:Uid="PowerLauncher_GenerateThumbnailsFromFiles">
|
<controls:SettingsCard x:Uid="PowerLauncher_GenerateThumbnailsFromFiles">
|
||||||
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.GenerateThumbnailsFromFiles, Mode=TwoWay}" />
|
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.GenerateThumbnailsFromFiles, Mode=TwoWay}" />
|
||||||
</controls:SettingsCard>
|
</controls:SettingsCard>
|
||||||
|
@ -736,6 +736,12 @@
|
|||||||
<data name="PowerLauncher_SearchResultPreference.Header" xml:space="preserve">
|
<data name="PowerLauncher_SearchResultPreference.Header" xml:space="preserve">
|
||||||
<value>Search result preference</value>
|
<value>Search result preference</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="PowerLauncher_UsePinyin.Header" xml:space="preserve">
|
||||||
|
<value>Use Pinyin</value>
|
||||||
|
</data>
|
||||||
|
<data name="PowerLauncher_UsePinyin.Description" xml:space="preserve">
|
||||||
|
<value>Experimental: Use Pinyin on the search query. May not work for every plugin.</value>
|
||||||
|
</data>
|
||||||
<data name="PowerLauncher_SearchResultPreference_MostRecentlyUsed" xml:space="preserve">
|
<data name="PowerLauncher_SearchResultPreference_MostRecentlyUsed" xml:space="preserve">
|
||||||
<value>Most recently used</value>
|
<value>Most recently used</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -577,6 +577,23 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool UsePinyin
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return settings.Properties.UsePinyin;
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (settings.Properties.UsePinyin != value)
|
||||||
|
{
|
||||||
|
settings.Properties.UsePinyin = value;
|
||||||
|
UpdateSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private ObservableCollection<PowerLauncherPluginViewModel> _plugins;
|
private ObservableCollection<PowerLauncherPluginViewModel> _plugins;
|
||||||
|
|
||||||
public ObservableCollection<PowerLauncherPluginViewModel> Plugins
|
public ObservableCollection<PowerLauncherPluginViewModel> Plugins
|
||||||
|
Loading…
Reference in New Issue
Block a user