diff --git a/.editorconfig b/.editorconfig
index 0e4883082c..83670fa830 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,373 +1,436 @@
-###############################################################################
-# EditorConfig is awesome: http://EditorConfig.org
-###############################################################################
+# Version: 1.6.2 (Using https://semver.org/)
+# Updated: 2020-11-02
+# See https://github.com/RehanSaeed/EditorConfig/releases for release notes.
+# See https://github.com/RehanSaeed/EditorConfig for updates to this file.
+# See http://EditorConfig.org for more information about .editorconfig files.
-###############################################################################
-# Top-most EditorConfig file
-###############################################################################
+##########################################
+# Common Settings
+##########################################
+
+# This file is the top-most EditorConfig file
root = true
-###############################################################################
-# Set default behavior to:
-# a UTF-8 encoding,
-# Unix-style line endings,
-# a newline ending the file,
-# 4 space indentation, and
-# trimming of trailing whitespace
-###############################################################################
+# All Files
[*]
charset = utf-8
-end_of_line = lf
-insert_final_newline = true
indent_style = space
indent_size = 4
+end_of_line = lf
+insert_final_newline = true
trim_trailing_whitespace = true
-###############################################################################
-# Set file behavior to:
-# 2 space indentation
-###############################################################################
-[*.{cmd,config,csproj,json,props,ps1,resx,sh,targets}]
-indent_size = 2
+##########################################
+# File Extension Settings
+##########################################
-###############################################################################
-# Set file behavior to:
-# Windows-style line endings, and
-# tabular indentation
-###############################################################################
+# Visual Studio Solution Files
[*.sln]
+indent_style = tab
+
+# Visual Studio XML Project Files
+[*.{csproj,vbproj,vcxproj.filters,proj,projitems,shproj}]
+indent_size = 2
+
+# T4 Templates Files
+[*.{tt,ttinclude}]
end_of_line = crlf
+
+# XML Configuration Files
+[*.{xml,config,props,targets,nuspec,resx,ruleset,vsixmanifest,vsct}]
+indent_size = 2
+
+# JSON Files
+[*.{json,json5,webmanifest}]
+indent_size = 2
+
+# YAML Files
+[*.{yml,yaml}]
+indent_size = 2
+
+# Markdown Files
+[*.md]
+trim_trailing_whitespace = false
+
+# Web Files
+[*.{htm,html,js,jsm,ts,tsx,css,sass,scss,less,svg,vue}]
+indent_size = 2
+
+# Batch Files
+[*.{cmd,bat}]
+end_of_line = crlf
+
+# Makefiles
+[Makefile]
indent_style = tab
-###############################################################################
-# Set dotnet naming rules to:
-# suggest async members be pascal case suffixed with Async
-# suggest const declarations be pascal case
-# suggest interfaces be pascal case prefixed with I
-# suggest parameters be camel case
-# suggest private and internal static fields be camel case
-# suggest private and internal fields be camel case
-# suggest public and protected declarations be pascal case
-# suggest static readonly declarations be pascal case
-# suggest type parameters be prefixed with T
-###############################################################################
-[*.cs]
-dotnet_naming_rule.async_members_should_be_pascal_case_suffixed_with_async.severity = suggestion
-dotnet_naming_rule.async_members_should_be_pascal_case_suffixed_with_async.style = pascal_case_suffixed_with_async
-dotnet_naming_rule.async_members_should_be_pascal_case_suffixed_with_async.symbols = async_members
-
-dotnet_naming_rule.const_declarations_should_be_pascal_case.severity = suggestion
-dotnet_naming_rule.const_declarations_should_be_pascal_case.style = pascal_case
-dotnet_naming_rule.const_declarations_should_be_pascal_case.symbols = const_declarations
-
-dotnet_naming_rule.interfaces_should_be_pascal_case_prefixed_with_i.severity = suggestion
-dotnet_naming_rule.interfaces_should_be_pascal_case_prefixed_with_i.style = pascal_case_prefixed_with_i
-dotnet_naming_rule.interfaces_should_be_pascal_case_prefixed_with_i.symbols = interfaces
-
-dotnet_naming_rule.parameters_should_be_camel_case.severity = suggestion
-dotnet_naming_rule.parameters_should_be_camel_case.style = camel_case
-dotnet_naming_rule.parameters_should_be_camel_case.symbols = parameters
-
-dotnet_naming_rule.private_and_internal_static_fields_should_be_camel_case.severity = suggestion
-dotnet_naming_rule.private_and_internal_static_fields_should_be_camel_case.style = camel_case
-dotnet_naming_rule.private_and_internal_static_fields_should_be_camel_case.symbols = private_and_internal_static_fields
-
-dotnet_naming_rule.private_and_internal_fields_should_be_camel_case.severity = suggestion
-dotnet_naming_rule.private_and_internal_fields_should_be_camel_case.style = camel_case
-dotnet_naming_rule.private_and_internal_fields_should_be_camel_case.symbols = private_and_internal_fields
-
-dotnet_naming_rule.public_and_protected_declarations_should_be_pascal_case.severity = suggestion
-dotnet_naming_rule.public_and_protected_declarations_should_be_pascal_case.style = pascal_case
-dotnet_naming_rule.public_and_protected_declarations_should_be_pascal_case.symbols = public_and_protected_declarations
-dotnet_naming_symbols.public_and_protected_declarations.applicable_kinds = method, field, event, property
-
-dotnet_naming_rule.static_readonly_declarations_should_be_pascal_case.severity = suggestion
-dotnet_naming_rule.static_readonly_declarations_should_be_pascal_case.style = pascal_case
-dotnet_naming_rule.static_readonly_declarations_should_be_pascal_case.symbols = static_readonly_declarations
-
-dotnet_naming_rule.type_parameters_should_be_pascal_case_prefixed_with_t.severity = suggestion
-dotnet_naming_rule.type_parameters_should_be_pascal_case_prefixed_with_t.style = pascal_case_prefixed_with_t
-dotnet_naming_rule.type_parameters_should_be_pascal_case_prefixed_with_t.symbols = type_parameters
-
-###############################################################################
-# Set dotnet naming styles to define:
-# camel case
-# pascal case
-# pascal case suffixed with Async
-# pascal case prefixed with I
-# pascal case prefixed with T
-###############################################################################
-[*.cs]
-dotnet_naming_style.camel_case.capitalization = camel_case
-
-dotnet_naming_style.pascal_case.capitalization = pascal_case
-
-dotnet_naming_style.pascal_case_suffixed_with_async.capitalization = pascal_case
-dotnet_naming_style.pascal_case_suffixed_with_async.required_suffix = Async
-
-dotnet_naming_style.pascal_case_prefixed_with_i.capitalization = pascal_case
-dotnet_naming_style.pascal_case_prefixed_with_i.required_prefix = I
-
-dotnet_naming_style.pascal_case_prefixed_with_t.capitalization = pascal_case
-dotnet_naming_style.pascal_case_prefixed_with_t.required_prefix = T
-
-###############################################################################
-# Set dotnet naming symbols to:
-# async members
-# const declarations
-# interfaces
-# private and internal fields
-# private and internal static fields
-# public and protected declarations
-# static readonly declarations
-# type parameters
-###############################################################################
-[*.cs]
-dotnet_naming_symbols.async_members.required_modifiers = async
-
-dotnet_naming_symbols.const_declarations.required_modifiers = const
-
-dotnet_naming_symbols.interfaces.applicable_kinds = interface
-
-dotnet_naming_symbols.parameters.applicable_kinds = parameter
-
-dotnet_naming_symbols.private_and_internal_fields.applicable_accessibilities = private, internal
-dotnet_naming_symbols.private_and_internal_fields.applicable_kinds = field
-
-dotnet_naming_symbols.private_and_internal_static_fields.applicable_accessibilities = private, internal
-dotnet_naming_symbols.private_and_internal_static_fields.applicable_kinds = field
-dotnet_naming_symbols.private_and_internal_static_fields.required_modifiers = static
-
-dotnet_naming_symbols.public_and_protected_declarations.applicable_accessibilities = public, protected
-
-dotnet_naming_symbols.static_readonly_declarations.required_modifiers = static, readonly
-
-dotnet_naming_symbols.type_parameters.applicable_kinds = type_parameter
-
-###############################################################################
-# Set dotnet sort options to:
-# do not separate import directives into groups, and
-# sort system directives first
-###############################################################################
-[*.cs]
-dotnet_separate_import_directive_groups = false
-dotnet_sort_system_directives_first = true
+##########################################
+# File Header (Uncomment to support file headers)
+# https://docs.microsoft.com/visualstudio/ide/reference/add-file-header
+##########################################
-###############################################################################
-# Set dotnet style options to:
-# suggest null-coalescing expressions,
-# suggest collection-initializers,
-# suggest explicit tuple names,
-# suggest null-propogation
-# suggest object-initializers,
-# generate parentheses in arithmetic binary operators for clarity,
-# generate parentheses in other binary operators for clarity,
-# don't generate parentheses in other operators if unnecessary,
-# generate parentheses in relational binary operators for clarity,
-# warn when not using predefined-types for locals, parameters, and members,
-# generate predefined-types of type names for member access,
-# generate auto properties,
-# suggest compound assignment,
-# generate conditional expression over assignment,
-# generate conditional expression over return,
-# suggest inferred anonymous types,
-# suggest inferred tuple names,
-# suggest 'is null' checks over '== null',
-# don't generate 'this.' and 'Me.' for events,
-# warn when not using 'this.' and 'Me.' for fields,
-# warn when not using 'this.' and 'Me.' for methods,
-# warn when not using 'this.' and 'Me.' for properties,
-# suggest readonly fields, and
-# generate accessibility modifiers for non interface members
-###############################################################################
-[*.cs]
-dotnet_style_coalesce_expression = true:suggestion
-dotnet_style_collection_initializer = true:suggestion
-dotnet_style_explicit_tuple_names = true:suggestion
-dotnet_style_null_propagation = true:suggestion
-dotnet_style_object_initializer = true:suggestion
-
-dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
-dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
-dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
-dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
+# [*.{cs,csx,cake,vb,vbx,tt,ttinclude}]
+file_header_template = Copyright (c) Six Labors.\nLicensed under the Apache License, Version 2.0.
-dotnet_style_predefined_type_for_locals_parameters_members = true:warning
-dotnet_style_predefined_type_for_member_access = true:silent
+# SA1636: File header copyright text should match
+# Justification: .editorconfig supports file headers. If this is changed to a value other than "none", a stylecop.json file will need to added to the project.
+# dotnet_diagnostic.SA1636.severity = none
-dotnet_style_prefer_auto_properties = true:silent
-dotnet_style_prefer_compound_assignment = true:suggestion
-dotnet_style_prefer_conditional_expression_over_assignment = true:silent
-dotnet_style_prefer_conditional_expression_over_return = true:silent
-dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
-dotnet_style_prefer_inferred_tuple_names = true:suggestion
-dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
+##########################################
+# .NET Language Conventions
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions
+##########################################
-dotnet_style_qualification_for_event = false:silent
+# .NET Code Style Settings
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#net-code-style-settings
+[*.{cs,csx,cake,vb,vbx}]
+# "this." and "Me." qualifiers
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#this-and-me
dotnet_style_qualification_for_field = true:warning
-dotnet_style_qualification_for_method = true:warning
dotnet_style_qualification_for_property = true:warning
-
-dotnet_style_readonly_field = true:suggestion
-dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
-
-###############################################################################
-# Set dotnet style options to:
-# suggest removing all unused parameters
-###############################################################################
-[*.cs]
-dotnet_code_quality_unused_parameters = all:suggestion
-
-###############################################################################
-# Set csharp indent options to:
-# indent block contents,
-# not indent braces,
-# indent case contents,
-# not indent case contents when block,
-# indent labels one less than the current, and
-# indent switch labels
-###############################################################################
-[*.cs]
-csharp_indent_block_contents = true
-csharp_indent_braces = false
-csharp_indent_case_contents = true
-csharp_indent_case_contents_when_block = false
-csharp_indent_labels = one_less_than_current
-csharp_indent_switch_labels = true
-
-###############################################################################
-# Set csharp new-line options to:
-# insert a new-line before "catch",
-# insert a new-line before "else",
-# insert a new-line before "finally",
-# insert a new-line before members in anonymous-types,
-# insert a new-line before members in object-initializers, and
-# insert a new-line before all open braces
-###############################################################################
-[*.cs]
-csharp_new_line_before_catch = true
+dotnet_style_qualification_for_method = true:warning
+dotnet_style_qualification_for_event = true:warning
+# Language keywords instead of framework type names for type references
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#language-keywords
+dotnet_style_predefined_type_for_locals_parameters_members = true:warning
+dotnet_style_predefined_type_for_member_access = true:warning
+# Modifier preferences
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#normalize-modifiers
+dotnet_style_require_accessibility_modifiers = always:warning
+csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:warning
+visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:warning
+dotnet_style_readonly_field = true:warning
+# Parentheses preferences
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#parentheses-preferences
+dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:warning
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:warning
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:warning
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary:suggestion
+# Expression-level preferences
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-level-preferences
+dotnet_style_object_initializer = true:warning
+dotnet_style_collection_initializer = true:warning
+dotnet_style_explicit_tuple_names = true:warning
+dotnet_style_prefer_inferred_tuple_names = true:warning
+dotnet_style_prefer_inferred_anonymous_type_member_names = true:warning
+dotnet_style_prefer_auto_properties = true:warning
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning
+dotnet_style_prefer_conditional_expression_over_assignment = false:suggestion
+dotnet_style_prefer_conditional_expression_over_return = false:suggestion
+dotnet_style_prefer_compound_assignment = true:warning
+# Null-checking preferences
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#null-checking-preferences
+dotnet_style_coalesce_expression = true:warning
+dotnet_style_null_propagation = true:warning
+# Parameter preferences
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#parameter-preferences
+dotnet_code_quality_unused_parameters = all:warning
+# More style options (Undocumented)
+# https://github.com/MicrosoftDocs/visualstudio-docs/issues/3641
+dotnet_style_operator_placement_when_wrapping = end_of_line
+# https://github.com/dotnet/roslyn/pull/40070
+dotnet_style_prefer_simplified_interpolation = true:warning
+
+# C# Code Style Settings
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#c-code-style-settings
+[*.{cs,csx,cake}]
+# Implicit and explicit types
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#implicit-and-explicit-types
+csharp_style_var_for_built_in_types = never
+csharp_style_var_when_type_is_apparent = true:warning
+csharp_style_var_elsewhere = false:warning
+# Expression-bodied members
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-bodied-members
+csharp_style_expression_bodied_methods = true:warning
+csharp_style_expression_bodied_constructors = true:warning
+csharp_style_expression_bodied_operators = true:warning
+csharp_style_expression_bodied_properties = true:warning
+csharp_style_expression_bodied_indexers = true:warning
+csharp_style_expression_bodied_accessors = true:warning
+csharp_style_expression_bodied_lambdas = true:warning
+csharp_style_expression_bodied_local_functions = true:warning
+# Pattern matching
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#pattern-matching
+csharp_style_pattern_matching_over_is_with_cast_check = true:warning
+csharp_style_pattern_matching_over_as_with_null_check = true:warning
+# Inlined variable declarations
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#inlined-variable-declarations
+csharp_style_inlined_variable_declaration = true:warning
+# Expression-level preferences
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-level-preferences
+csharp_prefer_simple_default_expression = true:warning
+# "Null" checking preferences
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#c-null-checking-preferences
+csharp_style_throw_expression = true:warning
+csharp_style_conditional_delegate_call = true:warning
+# Code block preferences
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#code-block-preferences
+csharp_prefer_braces = true:warning
+# Unused value preferences
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#unused-value-preferences
+csharp_style_unused_value_expression_statement_preference = discard_variable:suggestion
+csharp_style_unused_value_assignment_preference = discard_variable:suggestion
+# Index and range preferences
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#index-and-range-preferences
+csharp_style_prefer_index_operator = true:warning
+csharp_style_prefer_range_operator = true:warning
+# Miscellaneous preferences
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#miscellaneous-preferences
+csharp_style_deconstructed_variable_declaration = true:warning
+csharp_style_pattern_local_over_anonymous_function = true:warning
+csharp_using_directive_placement = outside_namespace:warning
+csharp_prefer_static_local_function = true:warning
+csharp_prefer_simple_using_statement = true:suggestion
+
+##########################################
+# .NET Formatting Conventions
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-code-style-settings-reference#formatting-conventions
+##########################################
+
+# Organize usings
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#organize-using-directives
+dotnet_sort_system_directives_first = true
+# Newline options
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#new-line-options
+csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
+csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
-
-csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
-
-csharp_new_line_before_open_brace = all
-
-###############################################################################
-# Set csharp preserve options to:
-# preserve single-line blocks, and
-# preserve single-line statements
-###############################################################################
-[*.cs]
-csharp_preserve_single_line_blocks = true
-csharp_preserve_single_line_statements = true
-
-###############################################################################
-# Set csharp space options to:
-# remove any space after a cast,
-# add a space after the colon in an inheritance clause,
-# add a space after a comma,
-# remove any space after a dot,
-# add a space after keywords in control flow statements,
-# add a space after a semicolon in a "for" statement,
-# add a space before and after binary operators,
-# remove space around declaration statements,
-# add a space before the colon in an inheritance clause,
-# remove any space before a comma,
-# remove any space before a dot,
-# remove any space before an open square-bracket,
-# remove any space before a semicolon in a "for" statement,
-# remove any space between empty square-brackets,
-# remove any space between a method call's empty parameter list parenthesis,
-# remove any space between a method call's name and its opening parenthesis,
-# remove any space between a method call's parameter list parenthesis,
-# remove any space between a method declaration's empty parameter list parenthesis,
-# remove any space between a method declaration's name and its openening parenthesis,
-# remove any space between a method declaration's parameter list parenthesis,
-# remove any space between parentheses, and
-# remove any space between square brackets
-###############################################################################
-[*.cs]
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_between_query_expression_clauses = true
+# Indentation options
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#indentation-options
+csharp_indent_case_contents = true
+csharp_indent_switch_labels = true
+csharp_indent_labels = no_change
+csharp_indent_block_contents = true
+csharp_indent_braces = false
+csharp_indent_case_contents_when_block = false
+# Spacing options
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#spacing-options
csharp_space_after_cast = false
-csharp_space_after_colon_in_inheritance_clause = true
-csharp_space_after_comma = true
-csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
-csharp_space_after_semicolon_in_for_statement = true
-
-csharp_space_around_binary_operators = before_and_after
-csharp_space_around_declaration_statements = do_not_ignore
-
+csharp_space_between_parentheses = false
csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_between_method_declaration_name_and_open_parenthesis = false
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_after_comma = true
csharp_space_before_comma = false
+csharp_space_after_dot = false
csharp_space_before_dot = false
-csharp_space_before_open_square_brackets = false
+csharp_space_after_semicolon_in_for_statement = true
csharp_space_before_semicolon_in_for_statement = false
-
+csharp_space_around_declaration_statements = false
+csharp_space_before_open_square_brackets = false
csharp_space_between_empty_square_brackets = false
-csharp_space_between_method_call_empty_parameter_list_parentheses = false
-csharp_space_between_method_call_name_and_opening_parenthesis = false
-csharp_space_between_method_call_parameter_list_parentheses = false
-csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
-csharp_space_between_method_declaration_name_and_open_parenthesis = false
-csharp_space_between_method_declaration_parameter_list_parentheses = false
-csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
+# Wrapping options
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#wrap-options
+csharp_preserve_single_line_statements = false
+csharp_preserve_single_line_blocks = true
-###############################################################################
-# Set csharp style options to:
-# generate braces,
-# suggest simple default expressions,
-# generate a preferred modifier order,
-# suggest conditional delegate calls,
-# suggest deconstructed variable declarations,
-# generate expression-bodied accessors,
-# generate expression-bodied constructors,
-# generate expression-bodied indexers,
-# generate expression-bodied lambdas,
-# generate expression-bodied methods,
-# generate expression-bodied operators,
-# generate expression-bodied properties,
-# suggest inlined variable declarations,
-# suggest local over anonymous functions,
-# suggest pattern-matching over "as" with "null" check,
-# suggest pattern-matching over "is" with "cast" check,
-# suggest throw expressions,
-# generate a discard variable for unused value expression statements,
-# suggest a discard variable for unused assignments,
-# warn when using var for built-in types,
-# warn when using var when the type is not apparent, and
-# warn when not using var when the type is apparent
-# warn when using simplified "using" declaration
-###############################################################################
-[*.cs]
-csharp_prefer_braces = true:silent
-csharp_prefer_simple_default_expression = true:suggestion
-csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
-
-csharp_style_conditional_delegate_call = true:suggestion
-csharp_style_deconstructed_variable_declaration = true:suggestion
-
-csharp_style_expression_bodied_accessors = true:silent
-csharp_style_expression_bodied_constructors = true:silent
-csharp_style_expression_bodied_indexers = true:silent
-csharp_style_expression_bodied_lambdas = true:silent
-csharp_style_expression_bodied_methods = true:silent
-csharp_style_expression_bodied_operators = true:silent
-csharp_style_expression_bodied_properties = true:silent
-
-csharp_style_inlined_variable_declaration = true:suggestion
-
-csharp_style_pattern_local_over_anonymous_function = true:suggestion
-csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
-csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
-
-csharp_style_throw_expression = true:suggestion
-
-csharp_style_unused_value_expression_statement_preference = discard_variable:silent
-csharp_style_unused_value_assignment_preference = discard_variable:suggestion
-
-csharp_style_var_for_built_in_types = never
-csharp_style_var_when_type_is_apparent = true:warning
-csharp_style_var_elsewhere = false:warning
+##########################################
+# .NET Naming Conventions
+# https://docs.microsoft.com/visualstudio/ide/editorconfig-naming-conventions
+##########################################
+
+[*.{cs,csx,cake,vb,vbx}]
+
+##########################################
+# Styles
+##########################################
+
+# camel_case_style - Define the camelCase style
+dotnet_naming_style.camel_case_style.capitalization = camel_case
+# pascal_case_style - Define the PascalCase style
+dotnet_naming_style.pascal_case_style.capitalization = pascal_case
+# first_upper_style - The first character must start with an upper-case character
+dotnet_naming_style.first_upper_style.capitalization = first_word_upper
+# prefix_interface_with_i_style - Interfaces must be PascalCase and the first character of an interface must be an 'I'
+dotnet_naming_style.prefix_interface_with_i_style.capitalization = pascal_case
+dotnet_naming_style.prefix_interface_with_i_style.required_prefix = I
+# prefix_type_parameters_with_t_style - Generic Type Parameters must be PascalCase and the first character must be a 'T'
+dotnet_naming_style.prefix_type_parameters_with_t_style.capitalization = pascal_case
+dotnet_naming_style.prefix_type_parameters_with_t_style.required_prefix = T
+# disallowed_style - Anything that has this style applied is marked as disallowed
+dotnet_naming_style.disallowed_style.capitalization = pascal_case
+dotnet_naming_style.disallowed_style.required_prefix = ____RULE_VIOLATION____
+dotnet_naming_style.disallowed_style.required_suffix = ____RULE_VIOLATION____
+# internal_error_style - This style should never occur... if it does, it indicates a bug in file or in the parser using the file
+dotnet_naming_style.internal_error_style.capitalization = pascal_case
+dotnet_naming_style.internal_error_style.required_prefix = ____INTERNAL_ERROR____
+dotnet_naming_style.internal_error_style.required_suffix = ____INTERNAL_ERROR____
+
+##########################################
+# .NET Design Guideline Field Naming Rules
+# Naming rules for fields follow the .NET Framework design guidelines
+# https://docs.microsoft.com/dotnet/standard/design-guidelines/index
+##########################################
+
+# All public/protected/protected_internal constant fields must be PascalCase
+# https://docs.microsoft.com/dotnet/standard/design-guidelines/field
+dotnet_naming_symbols.public_protected_constant_fields_group.applicable_accessibilities = public, protected, protected_internal
+dotnet_naming_symbols.public_protected_constant_fields_group.required_modifiers = const
+dotnet_naming_symbols.public_protected_constant_fields_group.applicable_kinds = field
+dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.symbols = public_protected_constant_fields_group
+dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.style = pascal_case_style
+dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.severity = warning
+
+# All public/protected/protected_internal static readonly fields must be PascalCase
+# https://docs.microsoft.com/dotnet/standard/design-guidelines/field
+dotnet_naming_symbols.public_protected_static_readonly_fields_group.applicable_accessibilities = public, protected, protected_internal
+dotnet_naming_symbols.public_protected_static_readonly_fields_group.required_modifiers = static, readonly
+dotnet_naming_symbols.public_protected_static_readonly_fields_group.applicable_kinds = field
+dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.symbols = public_protected_static_readonly_fields_group
+dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.style = pascal_case_style
+dotnet_naming_rule.public_protected_static_readonly_fields_must_be_pascal_case_rule.severity = warning
+
+# No other public/protected/protected_internal fields are allowed
+# https://docs.microsoft.com/dotnet/standard/design-guidelines/field
+dotnet_naming_symbols.other_public_protected_fields_group.applicable_accessibilities = public, protected, protected_internal
+dotnet_naming_symbols.other_public_protected_fields_group.applicable_kinds = field
+dotnet_naming_rule.other_public_protected_fields_disallowed_rule.symbols = other_public_protected_fields_group
+dotnet_naming_rule.other_public_protected_fields_disallowed_rule.style = disallowed_style
+dotnet_naming_rule.other_public_protected_fields_disallowed_rule.severity = error
+
+##########################################
+# StyleCop Field Naming Rules
+# Naming rules for fields follow the StyleCop analyzers
+# This does not override any rules using disallowed_style above
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers
+##########################################
+
+# All constant fields must be PascalCase
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1303.md
+dotnet_naming_symbols.stylecop_constant_fields_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected, private
+dotnet_naming_symbols.stylecop_constant_fields_group.required_modifiers = const
+dotnet_naming_symbols.stylecop_constant_fields_group.applicable_kinds = field
+dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.symbols = stylecop_constant_fields_group
+dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.style = pascal_case_style
+dotnet_naming_rule.stylecop_constant_fields_must_be_pascal_case_rule.severity = warning
+
+# All static readonly fields must be PascalCase
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1311.md
+dotnet_naming_symbols.stylecop_static_readonly_fields_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected, private
+dotnet_naming_symbols.stylecop_static_readonly_fields_group.required_modifiers = static, readonly
+dotnet_naming_symbols.stylecop_static_readonly_fields_group.applicable_kinds = field
+dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.symbols = stylecop_static_readonly_fields_group
+dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.style = pascal_case_style
+dotnet_naming_rule.stylecop_static_readonly_fields_must_be_pascal_case_rule.severity = warning
+
+# No non-private instance fields are allowed
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1401.md
+dotnet_naming_symbols.stylecop_fields_must_be_private_group.applicable_accessibilities = public, internal, protected_internal, protected, private_protected
+dotnet_naming_symbols.stylecop_fields_must_be_private_group.applicable_kinds = field
+dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.symbols = stylecop_fields_must_be_private_group
+dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.style = disallowed_style
+dotnet_naming_rule.stylecop_instance_fields_must_be_private_rule.severity = error
+
+# Private fields must be camelCase
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1306.md
+dotnet_naming_symbols.stylecop_private_fields_group.applicable_accessibilities = private
+dotnet_naming_symbols.stylecop_private_fields_group.applicable_kinds = field
+dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.symbols = stylecop_private_fields_group
+dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.style = camel_case_style
+dotnet_naming_rule.stylecop_private_fields_must_be_camel_case_rule.severity = warning
+
+# Local variables must be camelCase
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1312.md
+dotnet_naming_symbols.stylecop_local_fields_group.applicable_accessibilities = local
+dotnet_naming_symbols.stylecop_local_fields_group.applicable_kinds = local
+dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.symbols = stylecop_local_fields_group
+dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.style = camel_case_style
+dotnet_naming_rule.stylecop_local_fields_must_be_camel_case_rule.severity = silent
+
+# This rule should never fire. However, it's included for at least two purposes:
+# First, it helps to understand, reason about, and root-case certain types of issues, such as bugs in .editorconfig parsers.
+# Second, it helps to raise immediate awareness if a new field type is added (as occurred recently in C#).
+dotnet_naming_symbols.sanity_check_uncovered_field_case_group.applicable_accessibilities = *
+dotnet_naming_symbols.sanity_check_uncovered_field_case_group.applicable_kinds = field
+dotnet_naming_rule.sanity_check_uncovered_field_case_rule.symbols = sanity_check_uncovered_field_case_group
+dotnet_naming_rule.sanity_check_uncovered_field_case_rule.style = internal_error_style
+dotnet_naming_rule.sanity_check_uncovered_field_case_rule.severity = error
+
+
+##########################################
+# Other Naming Rules
+##########################################
+
+# All of the following must be PascalCase:
+# - Namespaces
+# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-namespaces
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1300.md
+# - Classes and Enumerations
+# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces
+# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1300.md
+# - Delegates
+# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces#names-of-common-types
+# - Constructors, Properties, Events, Methods
+# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-type-members
+dotnet_naming_symbols.element_group.applicable_kinds = namespace, class, enum, struct, delegate, event, method, property
+dotnet_naming_rule.element_rule.symbols = element_group
+dotnet_naming_rule.element_rule.style = pascal_case_style
+dotnet_naming_rule.element_rule.severity = warning
+
+# Interfaces use PascalCase and are prefixed with uppercase 'I'
+# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces
+dotnet_naming_symbols.interface_group.applicable_kinds = interface
+dotnet_naming_rule.interface_rule.symbols = interface_group
+dotnet_naming_rule.interface_rule.style = prefix_interface_with_i_style
+dotnet_naming_rule.interface_rule.severity = warning
+
+# Generics Type Parameters use PascalCase and are prefixed with uppercase 'T'
+# https://docs.microsoft.com/dotnet/standard/design-guidelines/names-of-classes-structs-and-interfaces
+dotnet_naming_symbols.type_parameter_group.applicable_kinds = type_parameter
+dotnet_naming_rule.type_parameter_rule.symbols = type_parameter_group
+dotnet_naming_rule.type_parameter_rule.style = prefix_type_parameters_with_t_style
+dotnet_naming_rule.type_parameter_rule.severity = warning
+
+# Function parameters use camelCase
+# https://docs.microsoft.com/dotnet/standard/design-guidelines/naming-parameters
+dotnet_naming_symbols.parameters_group.applicable_kinds = parameter
+dotnet_naming_rule.parameters_rule.symbols = parameters_group
+dotnet_naming_rule.parameters_rule.style = camel_case_style
+dotnet_naming_rule.parameters_rule.severity = warning
+
+##########################################
+# License
+##########################################
+# The following applies as to the .editorconfig file ONLY, and is
+# included below for reference, per the requirements of the license
+# corresponding to this .editorconfig file.
+# See: https://github.com/RehanSaeed/EditorConfig
+#
+# MIT License
+#
+# Copyright (c) 2017-2019 Muhammad Rehan Saeed
+# Copyright (c) 2019 Henry Gabryjelski
+#
+# 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.
+##########################################
diff --git a/.gitattributes b/.gitattributes
index c0bff6e189..416dd0d06f 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -2,9 +2,13 @@
# Set default behavior to:
# treat as text and
# normalize to Unix-style line endings
+###############################################################################
* text eol=lf
-
+###############################################################################
# Set explicit file behavior to:
+# treat as text and
+# normalize to Unix-style line endings
+###############################################################################
*.asm text eol=lf
*.c text eol=lf
*.clj text eol=lf
@@ -49,45 +53,58 @@
*.txt text eol=lf
*.vb text eol=lf
*.yml text eol=lf
+###############################################################################
+# Set explicit file behavior to:
# treat as text
# normalize to Unix-style line endings and
# diff as csharp
+###############################################################################
*.cs text eol=lf diff=csharp
+###############################################################################
+# Set explicit file behavior to:
+# treat as text
+# normalize to Unix-style line endings and
# use a union merge when resoling conflicts
+###############################################################################
*.csproj text eol=lf merge=union
*.dbproj text eol=lf merge=union
*.fsproj text eol=lf merge=union
*.ncrunchproject text eol=lf merge=union
*.vbproj text eol=lf merge=union
+###############################################################################
+# Set explicit file behavior to:
+# treat as text
# normalize to Windows-style line endings and
+# use a union merge when resoling conflicts
+###############################################################################
*.sln text eol=crlf merge=union
+###############################################################################
+# Set explicit file behavior to:
# treat as binary
+###############################################################################
*.basis binary
-*.bmp binary
-*.dds binary
*.dll binary
*.eot binary
*.exe binary
-*.gif binary
-*.jpg binary
*.ktx binary
*.otf binary
*.pbm binary
*.pdf binary
-*.png binary
*.ppt binary
*.pptx binary
*.pvr binary
*.snk binary
-*.tga binary
*.ttc binary
*.ttf binary
-*.webp binary
+*.wbmp binary
*.woff binary
*.woff2 binary
*.xls binary
*.xlsx binary
+###############################################################################
+# Set explicit file behavior to:
# diff as plain text
+###############################################################################
*.doc diff=astextplain
*.docx diff=astextplain
*.dot diff=astextplain
@@ -95,12 +112,16 @@
*.pptx diff=astextplain
*.rtf diff=astextplain
*.svg diff=astextplain
-*.jpg filter=lfs diff=lfs merge=lfs -text
-*.jpeg filter=lfs diff=lfs merge=lfs -text
-*.bmp filter=lfs diff=lfs merge=lfs -text
-*.gif filter=lfs diff=lfs merge=lfs -text
-*.png filter=lfs diff=lfs merge=lfs -text
-*.tif filter=lfs diff=lfs merge=lfs -text
-*.tiff filter=lfs diff=lfs merge=lfs -text
-*.tga filter=lfs diff=lfs merge=lfs -text
-*.webp filter=lfs diff=lfs merge=lfs -text
+###############################################################################
+# Handle image files by git lfs
+###############################################################################
+*.jpg filter=lfs diff=lfs merge=lfs -text
+*.jpeg filter=lfs diff=lfs merge=lfs -text
+*.bmp filter=lfs diff=lfs merge=lfs -text
+*.gif filter=lfs diff=lfs merge=lfs -text
+*.png filter=lfs diff=lfs merge=lfs -text
+*.tif filter=lfs diff=lfs merge=lfs -text
+*.tiff filter=lfs diff=lfs merge=lfs -text
+*.tga filter=lfs diff=lfs merge=lfs -text
+*.webp filter=lfs diff=lfs merge=lfs -text
+*.dds filter=lfs diff=lfs merge=lfs -text
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 89d1a75f27..0943ab4eb7 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -29,7 +29,7 @@
#### **Running tests and Debugging**
* Expected test output is pulled in as a submodule from the [ImageSharp.Tests.Images repository](https://github.com/SixLabors/Imagesharp.Tests.Images/tree/master/ReferenceOutput). To succesfully run tests, make sure that you have updated the submodules!
-* Debugging (running tests in Debug mode) is only supported on .NET Core 2.1, because of JIT Code Generation bugs like [dotnet/coreclr#16443](https://github.com/dotnet/coreclr/issues/16443) or [dotnet/coreclr#20657](https://github.com/dotnet/coreclr/issues/20657)
+* Debugging (running tests in Debug mode) is only supported on .NET Core 2.1+, because of JIT Code Generation bugs like [dotnet/coreclr#16443](https://github.com/dotnet/coreclr/issues/16443) or [dotnet/coreclr#20657](https://github.com/dotnet/coreclr/issues/20657)
#### **Do you have questions about consuming the library or the source code?**
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index cf9f787526..1326c72e86 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,7 +1,7 @@
blank_issues_enabled: false
contact_links:
- name: Ask a Question
- url: https://github.com/SixLabors/ImageSharp/discussions?discussions_q=category%3AHelp
+ url: https://github.com/SixLabors/ImageSharp/discussions?discussions_q=category%3AQ%26A
about: Ask a question about this project.
- name: Feature Request
url: https://github.com/SixLabors/ImageSharp/discussions?discussions_q=category%3AIdeas
diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
index 0e093a8347..4828f4f21f 100644
--- a/.github/workflows/build-and-test.yml
+++ b/.github/workflows/build-and-test.yml
@@ -15,13 +15,29 @@ jobs:
matrix:
options:
- os: ubuntu-latest
- framework: netcoreapp3.1
+ framework: net5.0
+ runtime: -x64
+ codecov: false
+ - os: macos-latest
+ framework: net5.0
runtime: -x64
codecov: false
- os: windows-latest
+ framework: net5.0
+ runtime: -x64
+ codecov: false
+ - os: ubuntu-latest
framework: netcoreapp3.1
runtime: -x64
codecov: true
+ - os: macos-latest
+ framework: netcoreapp3.1
+ runtime: -x64
+ codecov: false
+ - os: windows-latest
+ framework: netcoreapp3.1
+ runtime: -x64
+ codecov: false
- os: windows-latest
framework: netcoreapp2.1
runtime: -x64
@@ -40,6 +56,20 @@ jobs:
steps:
- uses: actions/checkout@v2
+
+ # See https://github.com/actions/checkout/issues/165#issuecomment-657673315
+ - name: Create LFS file list
+ run: git lfs ls-files -l | cut -d' ' -f1 | sort > .lfs-assets-id
+
+ - name: Restore LFS cache
+ uses: actions/cache@v2
+ id: lfs-cache
+ with:
+ path: .git/lfs
+ key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }}-v1
+
+ - name: Git LFS Pull
+ run: git lfs pull
- name: Install NuGet
uses: NuGet/setup-nuget@v1
@@ -52,17 +82,34 @@ jobs:
git fetch --prune --unshallow
git submodule -q update --init --recursive
+ - name: Setup NuGet Cache
+ uses: actions/cache@v2
+ id: nuget-cache
+ with:
+ path: ~/.nuget
+ key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props', '**/*.targets') }}
+ restore-keys: ${{ runner.os }}-nuget-
+
- name: Build
shell: pwsh
run: ./ci-build.ps1 "${{matrix.options.framework}}"
+ env:
+ SIXLABORS_TESTING: True
- name: Test
shell: pwsh
run: ./ci-test.ps1 "${{matrix.options.os}}" "${{matrix.options.framework}}" "${{matrix.options.runtime}}" "${{matrix.options.codecov}}"
env:
- CI: True
+ SIXLABORS_TESTING: True
XUNIT_PATH: .\tests\ImageSharp.Tests # Required for xunit
+ - name: Export Failed Output
+ uses: actions/upload-artifact@v2
+ if: failure()
+ with:
+ name: actual_output_${{ runner.os }}_${{ matrix.options.framework }}${{ matrix.options.runtime }}.zip
+ path: tests/Images/ActualOutput/
+
- name: Update Codecov
uses: codecov/codecov-action@v1
if: matrix.options.codecov == true && startsWith(github.repository, 'SixLabors')
diff --git a/.gitignore b/.gitignore
index a89cfcf104..475d6e76b0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -221,3 +221,4 @@ artifacts/
# Tests
**/Images/ActualOutput
**/Images/ReferenceOutput
+.DS_Store
diff --git a/.gitmodules b/.gitmodules
index 55389121f2..94d28dd526 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,7 +1,3 @@
-[submodule "tests/Images/External"]
- path = tests/Images/External
- url = https://github.com/SixLabors/Imagesharp.Tests.Images.git
- branch = master
[submodule "shared-infrastructure"]
path = shared-infrastructure
url = https://github.com/SixLabors/SharedInfrastructure
diff --git a/.runsettings b/.runsettings
new file mode 100644
index 0000000000..ca48342bd6
--- /dev/null
+++ b/.runsettings
@@ -0,0 +1,7 @@
+
+
+
+
+ category!=failing
+
+
diff --git a/Directory.Build.props b/Directory.Build.props
index 0f9c5bdde2..3a133efe7a 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -10,131 +10,12 @@
that is done by the file that imports us.
-->
-
- $(MSBuildThisFileDirectory)artifacts/
- $(SixLaborsProjectCategory)/$(MSBuildProjectName)
- https://github.com/SixLabors/ImageSharp/
+
+ $(MSBuildThisFileDirectory)
-
-
- true
- $(BaseArtifactsPath)obj/$(BaseArtifactsPathSuffix)/
- portable
- full
- disable
- true
- true
-
-
-
-
-
- $(DefineConstants);SUPPORTS_MATHF
- $(DefineConstants);SUPPORTS_HASHCODE
- $(DefineConstants);SUPPORTS_EXTENDED_INTRINSICS
- $(DefineConstants);SUPPORTS_SPAN_STREAM
- $(DefineConstants);SUPPORTS_ENCODING_STRING
- $(DefineConstants);SUPPORTS_RUNTIME_INTRINSICS
- $(DefineConstants);SUPPORTS_CODECOVERAGE
- $(DefineConstants);SUPPORTS_HOTPATH
- $(DefineConstants);SUPPORTS_CREATESPAN
-
-
- $(DefineConstants);SUPPORTS_MATHF
- $(DefineConstants);SUPPORTS_HASHCODE
- $(DefineConstants);SUPPORTS_EXTENDED_INTRINSICS
- $(DefineConstants);SUPPORTS_SPAN_STREAM
- $(DefineConstants);SUPPORTS_ENCODING_STRING
- $(DefineConstants);SUPPORTS_CODECOVERAGE
- $(DefineConstants);SUPPORTS_CREATESPAN
-
-
- $(DefineConstants);SUPPORTS_MATHF
- $(DefineConstants);SUPPORTS_CODECOVERAGE
- $(DefineConstants);SUPPORTS_CREATESPAN
-
-
- $(DefineConstants);SUPPORTS_MATHF
- $(DefineConstants);SUPPORTS_HASHCODE
- $(DefineConstants);SUPPORTS_SPAN_STREAM
- $(DefineConstants);SUPPORTS_ENCODING_STRING
- $(DefineConstants);SUPPORTS_CODECOVERAGE
- $(DefineConstants);SUPPORTS_CREATESPAN
-
-
- $(DefineConstants);SUPPORTS_CODECOVERAGE
-
-
- $(DefineConstants);SUPPORTS_EXTENDED_INTRINSICS
- $(DefineConstants);SUPPORTS_CODECOVERAGE
-
-
-
-
- Six Labors and contributors
- $(BaseArtifactsPath)bin/$(BaseArtifactsPathSuffix)/
- Six Labors
- $(BaseArtifactsPath)pkg/$(BaseArtifactsPathSuffix)/$(Configuration)/
- SixLabors.ImageSharp
- 0.0.1
- $(PackageVersion)
-
-
-
-
-
- v
- normal
-
-
-
-
- Copyright © Six Labors
- strict;IOperation
- true
- 8.0
- en
- true
- sixlabors.imagesharp.128.png
- Apache-2.0
- $(RepositoryUrl)
- true
- git
-
- https://www.myget.org/F/sixlabors/api/v3/index.json;
- https://api.nuget.org/v3/index.json;
-
- https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json;
-
- true
- $(MSBuildThisFileDirectory)shared-infrastructure/SixLabors.snk
- 00240000048000009400000006020000002400005253413100040000010001000147e6fe6766715eec6cfed61f1e7dcdbf69748a3e355c67e9d8dfd953acab1d5e012ba34b23308166fdc61ee1d0390d5f36d814a6091dd4b5ed9eda5a26afced924c683b4bfb4b3d64b0586a57eff9f02b1f84e3cb0ddd518bd1697f2c84dcbb97eb8bb5c7801be12112ed0ec86db934b0e9a5171e6bb1384b6d2f7d54dfa97
- true
-
-
-
-
-
-
-
-
-
-
-
+
+
diff --git a/Directory.Build.targets b/Directory.Build.targets
index 4e7ab9e6b7..9730219482 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -5,39 +5,12 @@
Directory.Build.targets is automatically picked up and imported by
Microsoft.Common.targets. This file needs to exist, even if empty so that
files in the parent directory tree, with the same name, are not imported
- instead. The import fairly late and most other props/targets will have been
+ instead. They import fairly late and most other props/targets will have been
imported beforehand. We also don't need to add ourselves to
MSBuildAllProjects, as that is done by the file that imports us.
-->
-
-
- $(DefineConstants);$(OS)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
diff --git a/ImageSharp.sln b/ImageSharp.sln
index 509dcf96bf..8dfab6033c 100644
--- a/ImageSharp.sln
+++ b/ImageSharp.sln
@@ -3,18 +3,18 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28902.138
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_root", "_root", "{C317F1B1-D75E-4C6D-83EB-80367343E0D7}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
.gitattributes = .gitattributes
.gitignore = .gitignore
.gitmodules = .gitmodules
+ .runsettings = .runsettings
ci-build.ps1 = ci-build.ps1
ci-pack.ps1 = ci-pack.ps1
ci-test.ps1 = ci-test.ps1
Directory.Build.props = Directory.Build.props
Directory.Build.targets = Directory.Build.targets
- GitVersion.yml = GitVersion.yml
LICENSE = LICENSE
README.md = README.md
EndProjectSection
@@ -45,6 +45,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{56801022
ProjectSection(SolutionItems) = preProject
tests\Directory.Build.props = tests\Directory.Build.props
tests\Directory.Build.targets = tests\Directory.Build.targets
+ tests\ImageSharp.Tests.ruleset = tests\ImageSharp.Tests.ruleset
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Images", "Images", "{FA55F5DE-11A6-487D-ABA4-BC93A02717DD}"
@@ -53,16 +54,23 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Input", "Input", "{9DA226A1
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Bmp", "Bmp", "{1A82C5F6-90E0-4E97-BE16-A825C046B493}"
ProjectSection(SolutionItems) = preProject
+ tests\Images\Input\Bmp\9S.BMP = tests\Images\Input\Bmp\9S.BMP
+ tests\Images\Input\Bmp\ba-bm.bmp = tests\Images\Input\Bmp\ba-bm.bmp
tests\Images\Input\Bmp\BitmapCoreHeaderQR.bmp = tests\Images\Input\Bmp\BitmapCoreHeaderQR.bmp
tests\Images\Input\Bmp\BITMAPV5HEADER.bmp = tests\Images\Input\Bmp\BITMAPV5HEADER.bmp
tests\Images\Input\Bmp\Car.bmp = tests\Images\Input\Bmp\Car.bmp
+ tests\Images\Input\Bmp\DIAMOND.BMP = tests\Images\Input\Bmp\DIAMOND.BMP
tests\Images\Input\Bmp\F.bmp = tests\Images\Input\Bmp\F.bmp
+ tests\Images\Input\Bmp\GMARBLE.BMP = tests\Images\Input\Bmp\GMARBLE.BMP
+ tests\Images\Input\Bmp\invalidPaletteSize.bmp = tests\Images\Input\Bmp\invalidPaletteSize.bmp
tests\Images\Input\Bmp\issue735.bmp = tests\Images\Input\Bmp\issue735.bmp
tests\Images\Input\Bmp\neg_height.bmp = tests\Images\Input\Bmp\neg_height.bmp
tests\Images\Input\Bmp\pal1.bmp = tests\Images\Input\Bmp\pal1.bmp
tests\Images\Input\Bmp\pal1p1.bmp = tests\Images\Input\Bmp\pal1p1.bmp
tests\Images\Input\Bmp\pal4.bmp = tests\Images\Input\Bmp\pal4.bmp
tests\Images\Input\Bmp\pal4rle.bmp = tests\Images\Input\Bmp\pal4rle.bmp
+ tests\Images\Input\Bmp\pal4rlecut.bmp = tests\Images\Input\Bmp\pal4rlecut.bmp
+ tests\Images\Input\Bmp\pal4rletrns.bmp = tests\Images\Input\Bmp\pal4rletrns.bmp
tests\Images\Input\Bmp\pal8-0.bmp = tests\Images\Input\Bmp\pal8-0.bmp
tests\Images\Input\Bmp\pal8gs.bmp = tests\Images\Input\Bmp\pal8gs.bmp
tests\Images\Input\Bmp\pal8offs.bmp = tests\Images\Input\Bmp\pal8offs.bmp
@@ -70,26 +78,45 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Bmp", "Bmp", "{1A82C5F6-90E
tests\Images\Input\Bmp\pal8os2v1_winv2.bmp = tests\Images\Input\Bmp\pal8os2v1_winv2.bmp
tests\Images\Input\Bmp\pal8os2v2-16.bmp = tests\Images\Input\Bmp\pal8os2v2-16.bmp
tests\Images\Input\Bmp\pal8os2v2.bmp = tests\Images\Input\Bmp\pal8os2v2.bmp
+ tests\Images\Input\Bmp\pal8oversizepal.bmp = tests\Images\Input\Bmp\pal8oversizepal.bmp
+ tests\Images\Input\Bmp\pal8rlecut.bmp = tests\Images\Input\Bmp\pal8rlecut.bmp
+ tests\Images\Input\Bmp\pal8rletrns.bmp = tests\Images\Input\Bmp\pal8rletrns.bmp
tests\Images\Input\Bmp\pal8v4.bmp = tests\Images\Input\Bmp\pal8v4.bmp
tests\Images\Input\Bmp\pal8v5.bmp = tests\Images\Input\Bmp\pal8v5.bmp
+ tests\Images\Input\Bmp\PINES.BMP = tests\Images\Input\Bmp\PINES.BMP
tests\Images\Input\Bmp\rgb16-565.bmp = tests\Images\Input\Bmp\rgb16-565.bmp
tests\Images\Input\Bmp\rgb16-565pal.bmp = tests\Images\Input\Bmp\rgb16-565pal.bmp
tests\Images\Input\Bmp\rgb16.bmp = tests\Images\Input\Bmp\rgb16.bmp
tests\Images\Input\Bmp\rgb16bfdef.bmp = tests\Images\Input\Bmp\rgb16bfdef.bmp
tests\Images\Input\Bmp\rgb24.bmp = tests\Images\Input\Bmp\rgb24.bmp
+ tests\Images\Input\Bmp\rgb24jpeg.bmp = tests\Images\Input\Bmp\rgb24jpeg.bmp
+ tests\Images\Input\Bmp\rgb24largepal.bmp = tests\Images\Input\Bmp\rgb24largepal.bmp
+ tests\Images\Input\Bmp\rgb24png.bmp = tests\Images\Input\Bmp\rgb24png.bmp
+ tests\Images\Input\Bmp\rgb24rle24.bmp = tests\Images\Input\Bmp\rgb24rle24.bmp
tests\Images\Input\Bmp\rgb32.bmp = tests\Images\Input\Bmp\rgb32.bmp
tests\Images\Input\Bmp\rgb32bf.bmp = tests\Images\Input\Bmp\rgb32bf.bmp
tests\Images\Input\Bmp\rgb32bfdef.bmp = tests\Images\Input\Bmp\rgb32bfdef.bmp
+ tests\Images\Input\Bmp\rgb32h52.bmp = tests\Images\Input\Bmp\rgb32h52.bmp
tests\Images\Input\Bmp\rgba32-1010102.bmp = tests\Images\Input\Bmp\rgba32-1010102.bmp
tests\Images\Input\Bmp\rgba32.bmp = tests\Images\Input\Bmp\rgba32.bmp
tests\Images\Input\Bmp\rgba32abf.bmp = tests\Images\Input\Bmp\rgba32abf.bmp
tests\Images\Input\Bmp\rgba32h56.bmp = tests\Images\Input\Bmp\rgba32h56.bmp
+ tests\Images\Input\Bmp\rgba32v4.bmp = tests\Images\Input\Bmp\rgba32v4.bmp
+ tests\Images\Input\Bmp\rle24rlecut.bmp = tests\Images\Input\Bmp\rle24rlecut.bmp
+ tests\Images\Input\Bmp\rle24rletrns.bmp = tests\Images\Input\Bmp\rle24rletrns.bmp
+ tests\Images\Input\Bmp\rle4-delta-320x240.bmp = tests\Images\Input\Bmp\rle4-delta-320x240.bmp
+ tests\Images\Input\Bmp\rle8-blank-160x120.bmp = tests\Images\Input\Bmp\rle8-blank-160x120.bmp
+ tests\Images\Input\Bmp\rle8-delta-320x240.bmp = tests\Images\Input\Bmp\rle8-delta-320x240.bmp
tests\Images\Input\Bmp\RunLengthEncoded-inverted.bmp = tests\Images\Input\Bmp\RunLengthEncoded-inverted.bmp
tests\Images\Input\Bmp\RunLengthEncoded.bmp = tests\Images\Input\Bmp\RunLengthEncoded.bmp
+ tests\Images\Input\Bmp\SKATER.BMP = tests\Images\Input\Bmp\SKATER.BMP
+ tests\Images\Input\Bmp\SPADE.BMP = tests\Images\Input\Bmp\SPADE.BMP
+ tests\Images\Input\Bmp\SUNFLOW.BMP = tests\Images\Input\Bmp\SUNFLOW.BMP
tests\Images\Input\Bmp\test16-inverted.bmp = tests\Images\Input\Bmp\test16-inverted.bmp
tests\Images\Input\Bmp\test16.bmp = tests\Images\Input\Bmp\test16.bmp
tests\Images\Input\Bmp\test8-inverted.bmp = tests\Images\Input\Bmp\test8-inverted.bmp
tests\Images\Input\Bmp\test8.bmp = tests\Images\Input\Bmp\test8.bmp
+ tests\Images\Input\Bmp\WARPD.BMP = tests\Images\Input\Bmp\WARPD.BMP
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Gif", "Gif", "{EE3FB0B3-1C31-41E9-93AB-BA800560A868}"
@@ -98,8 +125,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Gif", "Gif", "{EE3FB0B3-1C3
tests\Images\Input\Gif\base_4x1.gif = tests\Images\Input\Gif\base_4x1.gif
tests\Images\Input\Gif\cheers.gif = tests\Images\Input\Gif\cheers.gif
tests\Images\Input\Gif\giphy.gif = tests\Images\Input\Gif\giphy.gif
+ tests\Images\Input\Gif\GlobalQuantizationTest.gif = tests\Images\Input\Gif\GlobalQuantizationTest.gif
+ tests\Images\Input\Gif\image-zero-height.gif = tests\Images\Input\Gif\image-zero-height.gif
+ tests\Images\Input\Gif\image-zero-size.gif = tests\Images\Input\Gif\image-zero-size.gif
+ tests\Images\Input\Gif\image-zero-width.gif = tests\Images\Input\Gif\image-zero-width.gif
tests\Images\Input\Gif\kumin.gif = tests\Images\Input\Gif\kumin.gif
+ tests\Images\Input\Gif\large_comment.gif = tests\Images\Input\Gif\large_comment.gif
tests\Images\Input\Gif\leo.gif = tests\Images\Input\Gif\leo.gif
+ tests\Images\Input\Gif\max-height.gif = tests\Images\Input\Gif\max-height.gif
+ tests\Images\Input\Gif\max-width.gif = tests\Images\Input\Gif\max-width.gif
+ tests\Images\Input\Gif\receipt.gif = tests\Images\Input\Gif\receipt.gif
tests\Images\Input\Gif\rings.gif = tests\Images\Input\Gif\rings.gif
tests\Images\Input\Gif\trans.gif = tests\Images\Input\Gif\trans.gif
EndProjectSection
@@ -115,6 +150,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Jpg", "Jpg", "{DB21FED7-E8C
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "baseline", "baseline", "{195BA3D3-3E9F-4BC5-AB40-5F9FEB638146}"
ProjectSection(SolutionItems) = preProject
+ tests\Images\Input\Jpg\baseline\640px-Unequalized_Hawkes_Bay_NZ.jpg = tests\Images\Input\Jpg\baseline\640px-Unequalized_Hawkes_Bay_NZ.jpg
tests\Images\Input\Jpg\baseline\AsianCarvingLowContrast.jpg = tests\Images\Input\Jpg\baseline\AsianCarvingLowContrast.jpg
tests\Images\Input\Jpg\baseline\badeof.jpg = tests\Images\Input\Jpg\baseline\badeof.jpg
tests\Images\Input\Jpg\baseline\badrst.jpg = tests\Images\Input\Jpg\baseline\badrst.jpg
@@ -124,6 +160,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "baseline", "baseline", "{19
tests\Images\Input\Jpg\baseline\Floorplan.jpg = tests\Images\Input\Jpg\baseline\Floorplan.jpg
tests\Images\Input\Jpg\baseline\gamma_dalai_lama_gray.jpg = tests\Images\Input\Jpg\baseline\gamma_dalai_lama_gray.jpg
tests\Images\Input\Jpg\baseline\Hiyamugi.jpg = tests\Images\Input\Jpg\baseline\Hiyamugi.jpg
+ tests\Images\Input\Jpg\baseline\iptc-psAPP13-wIPTCempty.jpg = tests\Images\Input\Jpg\baseline\iptc-psAPP13-wIPTCempty.jpg
+ tests\Images\Input\Jpg\baseline\iptc.jpg = tests\Images\Input\Jpg\baseline\iptc.jpg
tests\Images\Input\Jpg\baseline\jpeg400jfif.jpg = tests\Images\Input\Jpg\baseline\jpeg400jfif.jpg
tests\Images\Input\Jpg\baseline\jpeg420exif.jpg = tests\Images\Input\Jpg\baseline\jpeg420exif.jpg
tests\Images\Input\Jpg\baseline\jpeg420small.jpg = tests\Images\Input\Jpg\baseline\jpeg420small.jpg
@@ -166,6 +204,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "JpegSnoopReports", "JpegSno
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "issues", "issues", "{5C9B689F-B96D-47BE-A208-C23B1B2A8570}"
ProjectSection(SolutionItems) = preProject
+ tests\Images\Input\Jpg\issues\issue-1076-invalid-subsampling.jpg = tests\Images\Input\Jpg\issues\issue-1076-invalid-subsampling.jpg
+ tests\Images\Input\Jpg\issues\issue-1221-identify-multi-frame.jpg = tests\Images\Input\Jpg\issues\issue-1221-identify-multi-frame.jpg
+ tests\Images\Input\Jpg\issues\issue1006-incorrect-resize.jpg = tests\Images\Input\Jpg\issues\issue1006-incorrect-resize.jpg
+ tests\Images\Input\Jpg\issues\issue1049-exif-resize.jpg = tests\Images\Input\Jpg\issues\issue1049-exif-resize.jpg
tests\Images\Input\Jpg\issues\Issue159-MissingFF00-Progressive-Bedroom.jpg = tests\Images\Input\Jpg\issues\Issue159-MissingFF00-Progressive-Bedroom.jpg
tests\Images\Input\Jpg\issues\Issue159-MissingFF00-Progressive-Girl.jpg = tests\Images\Input\Jpg\issues\Issue159-MissingFF00-Progressive-Girl.jpg
tests\Images\Input\Jpg\issues\Issue178-BadCoeffsProgressive-Lemon.jpg = tests\Images\Input\Jpg\issues\Issue178-BadCoeffsProgressive-Lemon.jpg
@@ -213,6 +255,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "fuzz", "fuzz", "{516A3532-6
tests\Images\Input\Jpg\issues\fuzz\Issue826-ArgumentException-C.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue826-ArgumentException-C.jpg
tests\Images\Input\Jpg\issues\fuzz\Issue827-AccessViolationException.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue827-AccessViolationException.jpg
tests\Images\Input\Jpg\issues\fuzz\Issue839-ExecutionEngineException.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue839-ExecutionEngineException.jpg
+ tests\Images\Input\Jpg\issues\fuzz\Issue922-AccessViolationException.jpg = tests\Images\Input\Jpg\issues\fuzz\Issue922-AccessViolationException.jpg
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "JpegSnoopReports", "JpegSnoopReports", "{714CDEA1-9AE6-4F76-B8B1-A7DB8C1DB82F}"
@@ -260,16 +303,24 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Png", "Png", "{E1C42A6F-913
ProjectSection(SolutionItems) = preProject
tests\Images\Input\Png\banner7-adam.png = tests\Images\Input\Png\banner7-adam.png
tests\Images\Input\Png\banner8-index.png = tests\Images\Input\Png\banner8-index.png
+ tests\Images\Input\Png\basn3p01.png = tests\Images\Input\Png\basn3p01.png
+ tests\Images\Input\Png\basn3p02.png = tests\Images\Input\Png\basn3p02.png
+ tests\Images\Input\Png\basn3p04.png = tests\Images\Input\Png\basn3p04.png
+ tests\Images\Input\Png\basn3p08.png = tests\Images\Input\Png\basn3p08.png
tests\Images\Input\Png\big-corrupted-chunk.png = tests\Images\Input\Png\big-corrupted-chunk.png
+ tests\Images\Input\Png\bike-small.png = tests\Images\Input\Png\bike-small.png
tests\Images\Input\Png\Bike.png = tests\Images\Input\Png\Bike.png
tests\Images\Input\Png\BikeGrayscale.png = tests\Images\Input\Png\BikeGrayscale.png
tests\Images\Input\Png\blur.png = tests\Images\Input\Png\blur.png
tests\Images\Input\Png\bpp1.png = tests\Images\Input\Png\bpp1.png
+ tests\Images\Input\Png\Bradley01.png = tests\Images\Input\Png\Bradley01.png
+ tests\Images\Input\Png\Bradley02.png = tests\Images\Input\Png\Bradley02.png
tests\Images\Input\Png\CalliphoraPartial.png = tests\Images\Input\Png\CalliphoraPartial.png
tests\Images\Input\Png\CalliphoraPartialGrayscale.png = tests\Images\Input\Png\CalliphoraPartialGrayscale.png
tests\Images\Input\Png\chunklength1.png = tests\Images\Input\Png\chunklength1.png
tests\Images\Input\Png\chunklength2.png = tests\Images\Input\Png\chunklength2.png
tests\Images\Input\Png\cross.png = tests\Images\Input\Png\cross.png
+ tests\Images\Input\Png\david.png = tests\Images\Input\Png\david.png
tests\Images\Input\Png\ducky.png = tests\Images\Input\Png\ducky.png
tests\Images\Input\Png\filter0.png = tests\Images\Input\Png\filter0.png
tests\Images\Input\Png\filter1.png = tests\Images\Input\Png\filter1.png
@@ -292,6 +343,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Png", "Png", "{E1C42A6F-913
tests\Images\Input\Png\iftbbn0g04.png = tests\Images\Input\Png\iftbbn0g04.png
tests\Images\Input\Png\indexed.png = tests\Images\Input\Png\indexed.png
tests\Images\Input\Png\interlaced.png = tests\Images\Input\Png\interlaced.png
+ tests\Images\Input\Png\InvalidTextData.png = tests\Images\Input\Png\InvalidTextData.png
tests\Images\Input\Png\kaboom.png = tests\Images\Input\Png\kaboom.png
tests\Images\Input\Png\low-variance.png = tests\Images\Input\Png\low-variance.png
tests\Images\Input\Png\palette-8bpp.png = tests\Images\Input\Png\palette-8bpp.png
@@ -299,6 +351,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Png", "Png", "{E1C42A6F-913
tests\Images\Input\Png\pd-source.png = tests\Images\Input\Png\pd-source.png
tests\Images\Input\Png\pd.png = tests\Images\Input\Png\pd.png
tests\Images\Input\Png\pl.png = tests\Images\Input\Png\pl.png
+ tests\Images\Input\Png\PngWithMetaData.png = tests\Images\Input\Png\PngWithMetaData.png
tests\Images\Input\Png\pp.png = tests\Images\Input\Png\pp.png
tests\Images\Input\Png\rainbow.png = tests\Images\Input\Png\rainbow.png
tests\Images\Input\Png\ratio-1x4.png = tests\Images\Input\Png\ratio-1x4.png
@@ -316,7 +369,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Png", "Png", "{E1C42A6F-913
tests\Images\Input\Png\versioning-1_2.png = tests\Images\Input\Png\versioning-1_2.png
tests\Images\Input\Png\vim16x16_1.png = tests\Images\Input\Png\vim16x16_1.png
tests\Images\Input\Png\vim16x16_2.png = tests\Images\Input\Png\vim16x16_2.png
+ tests\Images\Input\Png\xc1n0g08.png = tests\Images\Input\Png\xc1n0g08.png
+ tests\Images\Input\Png\xc9n2c08.png = tests\Images\Input\Png\xc9n2c08.png
+ tests\Images\Input\Png\xd0n2c08.png = tests\Images\Input\Png\xd0n2c08.png
+ tests\Images\Input\Png\xd3n2c08.png = tests\Images\Input\Png\xd3n2c08.png
+ tests\Images\Input\Png\xdtn0g01.png = tests\Images\Input\Png\xdtn0g01.png
tests\Images\Input\Png\zlib-overflow.png = tests\Images\Input\Png\zlib-overflow.png
+ tests\Images\Input\Png\zlib-overflow2.png = tests\Images\Input\Png\zlib-overflow2.png
+ tests\Images\Input\Png\zlib-ztxt-bad-header.png = tests\Images\Input\Png\zlib-ztxt-bad-header.png
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Tests", "tests\ImageSharp.Tests\ImageSharp.Tests.csproj", "{EA3000E9-2A91-4EC4-8A68-E566DEBDC4F6}"
@@ -332,6 +392,87 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "SharedInfrastructure", "sha
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageSharp.Tests.ProfilingSandbox", "tests\ImageSharp.Tests.ProfilingSandbox\ImageSharp.Tests.ProfilingSandbox.csproj", "{FC527290-2F22-432C-B77B-6E815726B02C}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "issues", "issues", "{670DD46C-82E9-499A-B2D2-00A802ED0141}"
+ ProjectSection(SolutionItems) = preProject
+ tests\Images\Input\Png\issues\Issue_1014_1.png = tests\Images\Input\Png\issues\Issue_1014_1.png
+ tests\Images\Input\Png\issues\Issue_1014_2.png = tests\Images\Input\Png\issues\Issue_1014_2.png
+ tests\Images\Input\Png\issues\Issue_1014_3.png = tests\Images\Input\Png\issues\Issue_1014_3.png
+ tests\Images\Input\Png\issues\Issue_1014_4.png = tests\Images\Input\Png\issues\Issue_1014_4.png
+ tests\Images\Input\Png\issues\Issue_1014_5.png = tests\Images\Input\Png\issues\Issue_1014_5.png
+ tests\Images\Input\Png\issues\Issue_1014_6.png = tests\Images\Input\Png\issues\Issue_1014_6.png
+ tests\Images\Input\Png\issues\Issue_1047.png = tests\Images\Input\Png\issues\Issue_1047.png
+ tests\Images\Input\Png\issues\Issue_1127.png = tests\Images\Input\Png\issues\Issue_1127.png
+ tests\Images\Input\Png\issues\Issue_1177_1.png = tests\Images\Input\Png\issues\Issue_1177_1.png
+ tests\Images\Input\Png\issues\Issue_1177_2.png = tests\Images\Input\Png\issues\Issue_1177_2.png
+ tests\Images\Input\Png\issues\Issue_410.png = tests\Images\Input\Png\issues\Issue_410.png
+ tests\Images\Input\Png\issues\Issue_935.png = tests\Images\Input\Png\issues\Issue_935.png
+ EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tga", "Tga", "{5DFC394F-136F-4B76-9BCA-3BA786515EFC}"
+ ProjectSection(SolutionItems) = preProject
+ tests\Images\Input\Tga\16bit_noalphabits.tga = tests\Images\Input\Tga\16bit_noalphabits.tga
+ tests\Images\Input\Tga\16bit_rle_noalphabits.tga = tests\Images\Input\Tga\16bit_rle_noalphabits.tga
+ tests\Images\Input\Tga\32bit_no_alphabits.tga = tests\Images\Input\Tga\32bit_no_alphabits.tga
+ tests\Images\Input\Tga\32bit_rle_no_alphabits.tga = tests\Images\Input\Tga\32bit_rle_no_alphabits.tga
+ tests\Images\Input\Tga\ccm8.tga = tests\Images\Input\Tga\ccm8.tga
+ tests\Images\Input\Tga\grayscale_a_LL.tga = tests\Images\Input\Tga\grayscale_a_LL.tga
+ tests\Images\Input\Tga\grayscale_a_LR.tga = tests\Images\Input\Tga\grayscale_a_LR.tga
+ tests\Images\Input\Tga\grayscale_a_rle_LL.tga = tests\Images\Input\Tga\grayscale_a_rle_LL.tga
+ tests\Images\Input\Tga\grayscale_a_rle_LR.tga = tests\Images\Input\Tga\grayscale_a_rle_LR.tga
+ tests\Images\Input\Tga\grayscale_a_rle_UL.tga = tests\Images\Input\Tga\grayscale_a_rle_UL.tga
+ tests\Images\Input\Tga\grayscale_a_rle_UR.tga = tests\Images\Input\Tga\grayscale_a_rle_UR.tga
+ tests\Images\Input\Tga\grayscale_a_UL.tga = tests\Images\Input\Tga\grayscale_a_UL.tga
+ tests\Images\Input\Tga\grayscale_a_UR.tga = tests\Images\Input\Tga\grayscale_a_UR.tga
+ tests\Images\Input\Tga\grayscale_LL.tga = tests\Images\Input\Tga\grayscale_LL.tga
+ tests\Images\Input\Tga\grayscale_LR.tga = tests\Images\Input\Tga\grayscale_LR.tga
+ tests\Images\Input\Tga\grayscale_rle_LR.tga = tests\Images\Input\Tga\grayscale_rle_LR.tga
+ tests\Images\Input\Tga\grayscale_rle_UL.tga = tests\Images\Input\Tga\grayscale_rle_UL.tga
+ tests\Images\Input\Tga\grayscale_rle_UR.tga = tests\Images\Input\Tga\grayscale_rle_UR.tga
+ tests\Images\Input\Tga\grayscale_UL.tga = tests\Images\Input\Tga\grayscale_UL.tga
+ tests\Images\Input\Tga\grayscale_UR.tga = tests\Images\Input\Tga\grayscale_UR.tga
+ tests\Images\Input\Tga\indexed_a_LL.tga = tests\Images\Input\Tga\indexed_a_LL.tga
+ tests\Images\Input\Tga\indexed_a_LR.tga = tests\Images\Input\Tga\indexed_a_LR.tga
+ tests\Images\Input\Tga\indexed_a_rle_LL.tga = tests\Images\Input\Tga\indexed_a_rle_LL.tga
+ tests\Images\Input\Tga\indexed_a_rle_LR.tga = tests\Images\Input\Tga\indexed_a_rle_LR.tga
+ tests\Images\Input\Tga\indexed_a_rle_UL.tga = tests\Images\Input\Tga\indexed_a_rle_UL.tga
+ tests\Images\Input\Tga\indexed_a_rle_UR.tga = tests\Images\Input\Tga\indexed_a_rle_UR.tga
+ tests\Images\Input\Tga\indexed_a_UL.tga = tests\Images\Input\Tga\indexed_a_UL.tga
+ tests\Images\Input\Tga\indexed_a_UR.tga = tests\Images\Input\Tga\indexed_a_UR.tga
+ tests\Images\Input\Tga\indexed_LR.tga = tests\Images\Input\Tga\indexed_LR.tga
+ tests\Images\Input\Tga\indexed_rle_LL.tga = tests\Images\Input\Tga\indexed_rle_LL.tga
+ tests\Images\Input\Tga\indexed_rle_LR.tga = tests\Images\Input\Tga\indexed_rle_LR.tga
+ tests\Images\Input\Tga\indexed_rle_UL.tga = tests\Images\Input\Tga\indexed_rle_UL.tga
+ tests\Images\Input\Tga\indexed_rle_UR.tga = tests\Images\Input\Tga\indexed_rle_UR.tga
+ tests\Images\Input\Tga\indexed_UL.tga = tests\Images\Input\Tga\indexed_UL.tga
+ tests\Images\Input\Tga\indexed_UR.tga = tests\Images\Input\Tga\indexed_UR.tga
+ tests\Images\Input\Tga\rgb15.tga = tests\Images\Input\Tga\rgb15.tga
+ tests\Images\Input\Tga\rgb15rle.tga = tests\Images\Input\Tga\rgb15rle.tga
+ tests\Images\Input\Tga\rgb24_top_left.tga = tests\Images\Input\Tga\rgb24_top_left.tga
+ tests\Images\Input\Tga\rgb_a_LL.tga = tests\Images\Input\Tga\rgb_a_LL.tga
+ tests\Images\Input\Tga\rgb_a_LR.tga = tests\Images\Input\Tga\rgb_a_LR.tga
+ tests\Images\Input\Tga\rgb_a_rle_LR.tga = tests\Images\Input\Tga\rgb_a_rle_LR.tga
+ tests\Images\Input\Tga\rgb_a_rle_UL.tga = tests\Images\Input\Tga\rgb_a_rle_UL.tga
+ tests\Images\Input\Tga\rgb_a_rle_UR.tga = tests\Images\Input\Tga\rgb_a_rle_UR.tga
+ tests\Images\Input\Tga\rgb_a_UL.tga = tests\Images\Input\Tga\rgb_a_UL.tga
+ tests\Images\Input\Tga\rgb_a_UR.tga = tests\Images\Input\Tga\rgb_a_UR.tga
+ tests\Images\Input\Tga\rgb_LR.tga = tests\Images\Input\Tga\rgb_LR.tga
+ tests\Images\Input\Tga\rgb_rle_LR.tga = tests\Images\Input\Tga\rgb_rle_LR.tga
+ tests\Images\Input\Tga\rgb_rle_UR.tga = tests\Images\Input\Tga\rgb_rle_UR.tga
+ tests\Images\Input\Tga\rgb_UR.tga = tests\Images\Input\Tga\rgb_UR.tga
+ tests\Images\Input\Tga\targa_16bit.tga = tests\Images\Input\Tga\targa_16bit.tga
+ tests\Images\Input\Tga\targa_16bit_pal.tga = tests\Images\Input\Tga\targa_16bit_pal.tga
+ tests\Images\Input\Tga\targa_16bit_rle.tga = tests\Images\Input\Tga\targa_16bit_rle.tga
+ tests\Images\Input\Tga\targa_24bit.tga = tests\Images\Input\Tga\targa_24bit.tga
+ tests\Images\Input\Tga\targa_24bit_pal.tga = tests\Images\Input\Tga\targa_24bit_pal.tga
+ tests\Images\Input\Tga\targa_24bit_pal_origin_topleft.tga = tests\Images\Input\Tga\targa_24bit_pal_origin_topleft.tga
+ tests\Images\Input\Tga\targa_24bit_rle.tga = tests\Images\Input\Tga\targa_24bit_rle.tga
+ tests\Images\Input\Tga\targa_24bit_rle_origin_topleft.tga = tests\Images\Input\Tga\targa_24bit_rle_origin_topleft.tga
+ tests\Images\Input\Tga\targa_32bit.tga = tests\Images\Input\Tga\targa_32bit.tga
+ tests\Images\Input\Tga\targa_32bit_rle.tga = tests\Images\Input\Tga\targa_32bit_rle.tga
+ tests\Images\Input\Tga\targa_8bit.tga = tests\Images\Input\Tga\targa_8bit.tga
+ tests\Images\Input\Tga\targa_8bit_rle.tga = tests\Images\Input\Tga\targa_8bit_rle.tga
+ EndProjectSection
+EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems*{2aa31a1f-142c-43f4-8687-09abca4b3a26}*SharedItemsImports = 5
@@ -399,6 +540,7 @@ Global
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
+ {1799C43E-5C54-4A8F-8D64-B1475241DB0D} = {C317F1B1-D75E-4C6D-83EB-80367343E0D7}
{FBE8C1AD-5AEC-4514-9B64-091D8E145865} = {1799C43E-5C54-4A8F-8D64-B1475241DB0D}
{2AA31A1F-142C-43F4-8687-09ABCA4B3A26} = {815C0625-CD3D-440F-9F80-2D83856AB7AE}
{FA55F5DE-11A6-487D-ABA4-BC93A02717DD} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
@@ -420,6 +562,8 @@ Global
{C0D7754B-5277-438E-ABEB-2BA34401B5A7} = {1799C43E-5C54-4A8F-8D64-B1475241DB0D}
{68A8CC40-6AED-4E96-B524-31B1158FDEEA} = {815C0625-CD3D-440F-9F80-2D83856AB7AE}
{FC527290-2F22-432C-B77B-6E815726B02C} = {56801022-D71A-4FBE-BC5B-CBA08E2284EC}
+ {670DD46C-82E9-499A-B2D2-00A802ED0141} = {E1C42A6F-913B-4A7B-B1A8-2BB62843B254}
+ {5DFC394F-136F-4B76-9BCA-3BA786515EFC} = {9DA226A1-8656-49A8-A58A-A8B5C081AD66}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5F8B9D1F-CD8B-4CC5-8216-D531E25BD795}
diff --git a/README.md b/README.md
index 6b2fa5d0f5..6cc8e53047 100644
--- a/README.md
+++ b/README.md
@@ -70,13 +70,15 @@ To clone ImageSharp locally, click the "Clone in [YOUR_OS]" button above or run
git clone https://github.com/SixLabors/ImageSharp
```
-If working with Windows please ensure that you have enabled log file paths in git (run as Administrator).
+If working with Windows please ensure that you have enabled long file paths in git (run as Administrator).
```bash
git config --system core.longpaths true
```
-This repository contains [git submodules](https://blog.github.com/2016-02-01-working-with-submodules/). To add the submodules to the project, navigate to the repository root and type:
+This repository uses [Git Large File Storage](https://docs.github.com/en/github/managing-large-files/installing-git-large-file-storage). Please follow the linked instructions to ensure you have it set up in your environment.
+
+This repository contains [Git Submodules](https://blog.github.com/2016-02-01-working-with-submodules/). To add the submodules to the project, navigate to the repository root and type:
``` bash
git submodule update --init --recursive
@@ -130,4 +132,4 @@ Become a bronze sponsor with a monthly donation of $100 and get your logo (small
-
\ No newline at end of file
+
diff --git a/shared-infrastructure b/shared-infrastructure
index b0d4cd9864..06a7339834 160000
--- a/shared-infrastructure
+++ b/shared-infrastructure
@@ -1 +1 @@
-Subproject commit b0d4cd98647996265a668e852574d901b27f22d6
+Subproject commit 06a733983486638b9e38197c7c6eb197ecac43e6
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 650f30fe1c..d211992a94 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -5,40 +5,22 @@
Directory.Build.props is automatically picked up and imported by
Microsoft.Common.props. This file needs to exist, even if empty so that
files in the parent directory tree, with the same name, are not imported
- instead. The import fairly early and only Sdk.props will have been
+ instead. They import fairly early and only Sdk.props will have been
imported beforehand. We also don't need to add ourselves to
MSBuildAllProjects, as that is done by the file that imports us.
-->
-
- $(MSBuildAllProjects);$(MSBuildThisFileDirectory)..\Directory.Build.props
- src
-
+
+
+
-
- $(MSBuildThisFileDirectory)..\shared-infrastructure\SixLabors.ruleset
- true
-
-
+
true
-
-
- true
- true
-
-
-
- true
-
- true
- snupkg
-
-
@@ -47,6 +29,4 @@
-
-
diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets
index d1875262d3..c15c2a90cc 100644
--- a/src/Directory.Build.targets
+++ b/src/Directory.Build.targets
@@ -5,82 +5,15 @@
Directory.Build.targets is automatically picked up and imported by
Microsoft.Common.targets. This file needs to exist, even if empty so that
files in the parent directory tree, with the same name, are not imported
- instead. The import fairly late and most other props/targets will have
+ instead. They import fairly late and most other props/targets will have
been imported beforehand. We also don't need to add ourselves to
MSBuildAllProjects, as that is done by the file that imports us.
-->
-
- $(MSBuildAllProjects);$(MSBuildThisFileDirectory)..\Directory.Build.targets
-
+
+
+
-
- $(IntermediateOutputPath)$(MSBuildProjectName).InternalsVisibleTo$(DefaultLanguageSourceExtension)
-
-
-
-
-
-
- <_LocalTopLevelSourceRoot Include="@(SourceRoot)" Condition="'%(SourceRoot.NestedRoot)' == ''"/>
-
-
-
-
-
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/ImageSharp/Color/Color.cs b/src/ImageSharp/Color/Color.cs
index 554fcb8354..72f16528a6 100644
--- a/src/ImageSharp/Color/Color.cs
+++ b/src/ImageSharp/Color/Color.cs
@@ -27,19 +27,19 @@ namespace SixLabors.ImageSharp
private Color(byte r, byte g, byte b, byte a)
{
this.data = new Rgba64(
- ImageMaths.UpscaleFrom8BitTo16Bit(r),
- ImageMaths.UpscaleFrom8BitTo16Bit(g),
- ImageMaths.UpscaleFrom8BitTo16Bit(b),
- ImageMaths.UpscaleFrom8BitTo16Bit(a));
+ ColorNumerics.UpscaleFrom8BitTo16Bit(r),
+ ColorNumerics.UpscaleFrom8BitTo16Bit(g),
+ ColorNumerics.UpscaleFrom8BitTo16Bit(b),
+ ColorNumerics.UpscaleFrom8BitTo16Bit(a));
}
[MethodImpl(InliningOptions.ShortMethod)]
private Color(byte r, byte g, byte b)
{
this.data = new Rgba64(
- ImageMaths.UpscaleFrom8BitTo16Bit(r),
- ImageMaths.UpscaleFrom8BitTo16Bit(g),
- ImageMaths.UpscaleFrom8BitTo16Bit(b),
+ ColorNumerics.UpscaleFrom8BitTo16Bit(r),
+ ColorNumerics.UpscaleFrom8BitTo16Bit(g),
+ ColorNumerics.UpscaleFrom8BitTo16Bit(b),
ushort.MaxValue);
}
diff --git a/src/ImageSharp/ColorSpaces/Cmyk.cs b/src/ImageSharp/ColorSpaces/Cmyk.cs
index 0aab295548..675f1f814c 100644
--- a/src/ImageSharp/ColorSpaces/Cmyk.cs
+++ b/src/ImageSharp/ColorSpaces/Cmyk.cs
@@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.ColorSpaces
[MethodImpl(InliningOptions.ShortMethod)]
public Cmyk(Vector4 vector)
{
- vector = Vector4Utilities.FastClamp(vector, Min, Max);
+ vector = Numerics.Clamp(vector, Min, Max);
this.C = vector.X;
this.M = vector.Y;
this.Y = vector.Z;
diff --git a/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs
index 719565fd81..5cd89abfd6 100644
--- a/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs
+++ b/src/ImageSharp/ColorSpaces/Companding/LCompanding.cs
@@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
/// The representing the linear channel value.
[MethodImpl(InliningOptions.ShortMethod)]
public static float Expand(float channel)
- => channel <= 0.08F ? (100F * channel) / CieConstants.Kappa : ImageMaths.Pow3((channel + 0.16F) / 1.16F);
+ => channel <= 0.08F ? (100F * channel) / CieConstants.Kappa : Numerics.Pow3((channel + 0.16F) / 1.16F);
///
/// Compresses an uncompanded channel (linear) to its nonlinear equivalent.
diff --git a/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs b/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs
index 2e212ad19f..dc6c960aa5 100644
--- a/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs
+++ b/src/ImageSharp/ColorSpaces/Companding/SRgbCompanding.cs
@@ -1,10 +1,14 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+#if SUPPORTS_RUNTIME_INTRINSICS
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+#endif
namespace SixLabors.ImageSharp.ColorSpaces.Companding
{
@@ -18,19 +22,83 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
///
public static class SRgbCompanding
{
+ private const int Length = Scale + 2; // 256kb @ 16bit precision.
+ private const int Scale = (1 << 16) - 1;
+
+ private static readonly Lazy LazyCompressTable = new Lazy(
+ () =>
+ {
+ var result = new float[Length];
+
+ for (int i = 0; i < result.Length; i++)
+ {
+ double d = (double)i / Scale;
+ if (d <= (0.04045 / 12.92))
+ {
+ d *= 12.92;
+ }
+ else
+ {
+ d = (1.055 * Math.Pow(d, 1.0 / 2.4)) - 0.055;
+ }
+
+ result[i] = (float)d;
+ }
+
+ return result;
+ },
+ true);
+
+ private static readonly Lazy LazyExpandTable = new Lazy(
+ () =>
+ {
+ var result = new float[Length];
+
+ for (int i = 0; i < result.Length; i++)
+ {
+ double d = (double)i / Scale;
+ if (d <= 0.04045)
+ {
+ d /= 12.92;
+ }
+ else
+ {
+ d = Math.Pow((d + 0.055) / 1.055, 2.4);
+ }
+
+ result[i] = (float)d;
+ }
+
+ return result;
+ },
+ true);
+
+ private static float[] ExpandTable => LazyExpandTable.Value;
+
+ private static float[] CompressTable => LazyCompressTable.Value;
+
///
/// Expands the companded vectors to their linear equivalents with respect to the energy.
///
/// The span of vectors.
- [MethodImpl(InliningOptions.ShortMethod)]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Expand(Span vectors)
{
- ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors);
+#if SUPPORTS_RUNTIME_INTRINSICS
+ if (Avx2.IsSupported && vectors.Length >= 2)
+ {
+ CompandAvx2(vectors, ExpandTable);
- for (int i = 0; i < vectors.Length; i++)
+ if (Numerics.Modulo2(vectors.Length) != 0)
+ {
+ // Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
+ Expand(ref MemoryMarshal.GetReference(vectors.Slice(vectors.Length - 1)));
+ }
+ }
+ else
+#endif
{
- ref Vector4 v = ref Unsafe.Add(ref baseRef, i);
- Expand(ref v);
+ CompandScalar(vectors, ExpandTable);
}
}
@@ -38,15 +106,24 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
/// Compresses the uncompanded vectors to their nonlinear equivalents with respect to the energy.
///
/// The span of vectors.
- [MethodImpl(InliningOptions.ShortMethod)]
- public static void Compress(Span vectors)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe void Compress(Span vectors)
{
- ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors);
+#if SUPPORTS_RUNTIME_INTRINSICS
+ if (Avx2.IsSupported && vectors.Length >= 2)
+ {
+ CompandAvx2(vectors, CompressTable);
- for (int i = 0; i < vectors.Length; i++)
+ if (Numerics.Modulo2(vectors.Length) != 0)
+ {
+ // Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
+ Compress(ref MemoryMarshal.GetReference(vectors.Slice(vectors.Length - 1)));
+ }
+ }
+ else
+#endif
{
- ref Vector4 v = ref Unsafe.Add(ref baseRef, i);
- Compress(ref v);
+ CompandScalar(vectors, CompressTable);
}
}
@@ -54,9 +131,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
/// Expands a companded vector to its linear equivalent with respect to the energy.
///
/// The vector.
- [MethodImpl(InliningOptions.ShortMethod)]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Expand(ref Vector4 vector)
{
+ // Alpha is already a linear representation of opacity so we do not want to convert it.
vector.X = Expand(vector.X);
vector.Y = Expand(vector.Y);
vector.Z = Expand(vector.Z);
@@ -66,9 +144,10 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
/// Compresses an uncompanded vector (linear) to its nonlinear equivalent.
///
/// The vector.
- [MethodImpl(InliningOptions.ShortMethod)]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Compress(ref Vector4 vector)
{
+ // Alpha is already a linear representation of opacity so we do not want to convert it.
vector.X = Compress(vector.X);
vector.Y = Compress(vector.Y);
vector.Z = Compress(vector.Z);
@@ -79,15 +158,84 @@ namespace SixLabors.ImageSharp.ColorSpaces.Companding
///
/// The channel value.
/// The representing the linear channel value.
- [MethodImpl(InliningOptions.ShortMethod)]
- public static float Expand(float channel) => channel <= 0.04045F ? channel / 12.92F : MathF.Pow((channel + 0.055F) / 1.055F, 2.4F);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Expand(float channel)
+ => channel <= 0.04045F ? channel / 12.92F : MathF.Pow((channel + 0.055F) / 1.055F, 2.4F);
///
/// Compresses an uncompanded channel (linear) to its nonlinear equivalent.
///
/// The channel value.
/// The representing the nonlinear channel value.
- [MethodImpl(InliningOptions.ShortMethod)]
- public static float Compress(float channel) => channel <= 0.0031308F ? 12.92F * channel : (1.055F * MathF.Pow(channel, 0.416666666666667F)) - 0.055F;
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Compress(float channel)
+ => channel <= 0.0031308F ? 12.92F * channel : (1.055F * MathF.Pow(channel, 0.416666666666667F)) - 0.055F;
+
+#if SUPPORTS_RUNTIME_INTRINSICS
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static unsafe void CompandAvx2(Span vectors, float[] table)
+ {
+ fixed (float* tablePointer = &table[0])
+ {
+ var scale = Vector256.Create((float)Scale);
+ Vector256 zero = Vector256.Zero;
+ var offset = Vector256.Create(1);
+
+ // Divide by 2 as 4 elements per Vector4 and 8 per Vector256
+ ref Vector256 vectorsBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(vectors));
+ ref Vector256 vectorsLast = ref Unsafe.Add(ref vectorsBase, (IntPtr)((uint)vectors.Length / 2u));
+
+ while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast))
+ {
+ Vector256 multiplied = Avx.Multiply(scale, vectorsBase);
+ multiplied = Avx.Min(Avx.Max(zero, multiplied), scale);
+
+ Vector256 truncated = Avx.ConvertToVector256Int32WithTruncation(multiplied);
+ Vector256 truncatedF = Avx.ConvertToVector256Single(truncated);
+
+ Vector256 low = Avx2.GatherVector256(tablePointer, truncated, sizeof(float));
+ Vector256 high = Avx2.GatherVector256(tablePointer, Avx2.Add(truncated, offset), sizeof(float));
+
+ // Alpha is already a linear representation of opacity so we do not want to convert it.
+ Vector256 companded = Numerics.Lerp(low, high, Avx.Subtract(multiplied, truncatedF));
+ vectorsBase = Avx.Blend(companded, vectorsBase, Numerics.BlendAlphaControl);
+ vectorsBase = ref Unsafe.Add(ref vectorsBase, 1);
+ }
+ }
+ }
+#endif
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static unsafe void CompandScalar(Span vectors, float[] table)
+ {
+ fixed (float* tablePointer = &table[0])
+ {
+ Vector4 zero = Vector4.Zero;
+ var scale = new Vector4(Scale);
+ ref Vector4 vectorsBase = ref MemoryMarshal.GetReference(vectors);
+ ref Vector4 vectorsLast = ref Unsafe.Add(ref vectorsBase, vectors.Length);
+
+ while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast))
+ {
+ Vector4 multiplied = Numerics.Clamp(vectorsBase * Scale, zero, scale);
+
+ float f0 = multiplied.X;
+ float f1 = multiplied.Y;
+ float f2 = multiplied.Z;
+
+ uint i0 = (uint)f0;
+ uint i1 = (uint)f1;
+ uint i2 = (uint)f2;
+
+ // Alpha is already a linear representation of opacity so we do not want to convert it.
+ vectorsBase.X = Numerics.Lerp(tablePointer[i0], tablePointer[i0 + 1], f0 - (int)i0);
+ vectorsBase.Y = Numerics.Lerp(tablePointer[i1], tablePointer[i1 + 1], f1 - (int)i1);
+ vectorsBase.Z = Numerics.Lerp(tablePointer[i2], tablePointer[i2 + 1], f2 - (int)i2);
+
+ vectorsBase = ref Unsafe.Add(ref vectorsBase, 1);
+ }
+ }
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs
index 31c3f46330..34354efe54 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLabToCieXyzConverter.cs
@@ -25,11 +25,11 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
float fx = (a / 500F) + fy;
float fz = fy - (b / 200F);
- float fx3 = ImageMaths.Pow3(fx);
- float fz3 = ImageMaths.Pow3(fz);
+ float fx3 = Numerics.Pow3(fx);
+ float fz3 = Numerics.Pow3(fz);
float xr = fx3 > CieConstants.Epsilon ? fx3 : ((116F * fx) - 16F) / CieConstants.Kappa;
- float yr = l > CieConstants.Kappa * CieConstants.Epsilon ? ImageMaths.Pow3((l + 16F) / 116F) : l / CieConstants.Kappa;
+ float yr = l > CieConstants.Kappa * CieConstants.Epsilon ? Numerics.Pow3((l + 16F) / 116F) : l / CieConstants.Kappa;
float zr = fz3 > CieConstants.Epsilon ? fz3 : ((116F * fz) - 16F) / CieConstants.Kappa;
var wxyz = new Vector3(input.WhitePoint.X, input.WhitePoint.Y, input.WhitePoint.Z);
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs
index 7f15fc77d8..12c65105fc 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/CieLuvToCieXyzConverter.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Runtime.CompilerServices;
@@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
float v0 = ComputeV0(input.WhitePoint);
float y = l > CieConstants.Kappa * CieConstants.Epsilon
- ? ImageMaths.Pow3((l + 16) / 116)
+ ? Numerics.Pow3((l + 16) / 116)
: l / CieConstants.Kappa;
float a = ((52 * l / (u + (13 * l * u0))) - 1) / 3;
@@ -71,4 +71,4 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
private static float ComputeV0(in CieXyz input)
=> (9 * input.Y) / (input.X + (15 * input.Y) + (3 * input.Z));
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs
index 4c3cdba224..f120d6f3dd 100644
--- a/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs
+++ b/src/ImageSharp/ColorSpaces/Conversion/Implementation/Converters/HunterLabToCieXyzConverter.cs
@@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.ColorSpaces.Conversion
float ka = ComputeKa(input.WhitePoint);
float kb = ComputeKb(input.WhitePoint);
- float pow = ImageMaths.Pow2(l / 100F);
+ float pow = Numerics.Pow2(l / 100F);
float sqrtPow = MathF.Sqrt(pow);
float y = pow * yn;
diff --git a/src/ImageSharp/ColorSpaces/YCbCr.cs b/src/ImageSharp/ColorSpaces/YCbCr.cs
index eaaf7f58f4..b39fe30252 100644
--- a/src/ImageSharp/ColorSpaces/YCbCr.cs
+++ b/src/ImageSharp/ColorSpaces/YCbCr.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System;
@@ -100,4 +100,4 @@ namespace SixLabors.ImageSharp.ColorSpaces
&& this.Cr.Equals(other.Cr);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/ImageSharp/Common/Extensions/ComparableExtensions.cs b/src/ImageSharp/Common/Extensions/ComparableExtensions.cs
deleted file mode 100644
index ef3d1deac3..0000000000
--- a/src/ImageSharp/Common/Extensions/ComparableExtensions.cs
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
-
-using System;
-using System.Runtime.CompilerServices;
-
-namespace SixLabors.ImageSharp
-{
- ///
- /// Extension methods for classes that implement .
- ///
- internal static class ComparableExtensions
- {
- ///
- /// Restricts a to be within a specified range.
- ///
- /// The value to clamp.
- /// The minimum value. If value is less than min, min will be returned.
- /// The maximum value. If value is greater than max, max will be returned.
- ///
- /// The representing the clamped value.
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public static byte Clamp(this byte value, byte min, byte max)
- {
- // Order is important here as someone might set min to higher than max.
- if (value >= max)
- {
- return max;
- }
-
- if (value <= min)
- {
- return min;
- }
-
- return value;
- }
-
- ///
- /// Restricts a to be within a specified range.
- ///
- /// The The value to clamp.
- /// The minimum value. If value is less than min, min will be returned.
- /// The maximum value. If value is greater than max, max will be returned.
- ///
- /// The representing the clamped value.
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public static uint Clamp(this uint value, uint min, uint max)
- {
- if (value >= max)
- {
- return max;
- }
-
- if (value <= min)
- {
- return min;
- }
-
- return value;
- }
-
- ///
- /// Restricts a to be within a specified range.
- ///
- /// The The value to clamp.
- /// The minimum value. If value is less than min, min will be returned.
- /// The maximum value. If value is greater than max, max will be returned.
- ///
- /// The representing the clamped value.
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public static int Clamp(this int value, int min, int max)
- {
- if (value >= max)
- {
- return max;
- }
-
- if (value <= min)
- {
- return min;
- }
-
- return value;
- }
-
- ///
- /// Restricts a to be within a specified range.
- ///
- /// The The value to clamp.
- /// The minimum value. If value is less than min, min will be returned.
- /// The maximum value. If value is greater than max, max will be returned.
- ///
- /// The representing the clamped value.
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public static float Clamp(this float value, float min, float max)
- {
- if (value >= max)
- {
- return max;
- }
-
- if (value <= min)
- {
- return min;
- }
-
- return value;
- }
-
- ///
- /// Restricts a to be within a specified range.
- ///
- /// The The value to clamp.
- /// The minimum value. If value is less than min, min will be returned.
- /// The maximum value. If value is greater than max, max will be returned.
- ///
- /// The representing the clamped value.
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public static double Clamp(this double value, double min, double max)
- {
- if (value >= max)
- {
- return max;
- }
-
- if (value <= min)
- {
- return min;
- }
-
- return value;
- }
- }
-}
diff --git a/src/ImageSharp/Common/Helpers/Buffer2DUtils.cs b/src/ImageSharp/Common/Helpers/Buffer2DUtils.cs
deleted file mode 100644
index f4811d6ca8..0000000000
--- a/src/ImageSharp/Common/Helpers/Buffer2DUtils.cs
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
-
-using System;
-using System.Numerics;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-using SixLabors.ImageSharp.Memory;
-using SixLabors.ImageSharp.PixelFormats;
-
-namespace SixLabors.ImageSharp
-{
- ///
- /// Extension methods for .
- /// TODO: One day rewrite all this to use SIMD intrinsics. There's a lot of scope for improvement.
- ///
- internal static class Buffer2DUtils
- {
- ///
- /// Computes the sum of vectors in weighted by the kernel weight values.
- ///
- /// The pixel format.
- /// The 1D convolution kernel.
- /// The source frame.
- /// The target row.
- /// The current row.
- /// The current column.
- /// The minimum working area row.
- /// The maximum working area row.
- /// The minimum working area column.
- /// The maximum working area column.
- public static void Convolve4(
- Span kernel,
- Buffer2D sourcePixels,
- Span targetRow,
- int row,
- int column,
- int minRow,
- int maxRow,
- int minColumn,
- int maxColumn)
- where TPixel : unmanaged, IPixel
- {
- ComplexVector4 vector = default;
- int kernelLength = kernel.Length;
- int radiusY = kernelLength >> 1;
- int sourceOffsetColumnBase = column + minColumn;
- ref Complex64 baseRef = ref MemoryMarshal.GetReference(kernel);
-
- for (int i = 0; i < kernelLength; i++)
- {
- int offsetY = (row + i - radiusY).Clamp(minRow, maxRow);
- int offsetX = sourceOffsetColumnBase.Clamp(minColumn, maxColumn);
- Span sourceRowSpan = sourcePixels.GetRowSpan(offsetY);
- var currentColor = sourceRowSpan[offsetX].ToVector4();
-
- vector.Sum(Unsafe.Add(ref baseRef, i) * currentColor);
- }
-
- targetRow[column] = vector;
- }
-
- ///
- /// Computes the sum of vectors in weighted by the kernel weight values and accumulates the partial results.
- ///
- /// The 1D convolution kernel.
- /// The source frame.
- /// The target row.
- /// The current row.
- /// The current column.
- /// The minimum working area row.
- /// The maximum working area row.
- /// The minimum working area column.
- /// The maximum working area column.
- /// The weight factor for the real component of the complex pixel values.
- /// The weight factor for the imaginary component of the complex pixel values.
- public static void Convolve4AndAccumulatePartials(
- Span kernel,
- Buffer2D sourceValues,
- Span targetRow,
- int row,
- int column,
- int minRow,
- int maxRow,
- int minColumn,
- int maxColumn,
- float z,
- float w)
- {
- ComplexVector4 vector = default;
- int kernelLength = kernel.Length;
- int radiusX = kernelLength >> 1;
- int sourceOffsetColumnBase = column + minColumn;
-
- int offsetY = row.Clamp(minRow, maxRow);
- ref ComplexVector4 sourceRef = ref MemoryMarshal.GetReference(sourceValues.GetRowSpan(offsetY));
- ref Complex64 baseRef = ref MemoryMarshal.GetReference(kernel);
-
- for (int x = 0; x < kernelLength; x++)
- {
- int offsetX = (sourceOffsetColumnBase + x - radiusX).Clamp(minColumn, maxColumn);
- vector.Sum(Unsafe.Add(ref baseRef, x) * Unsafe.Add(ref sourceRef, offsetX));
- }
-
- targetRow[column] += vector.WeightedSum(z, w);
- }
- }
-}
diff --git a/src/ImageSharp/Common/Helpers/ColorNumerics.cs b/src/ImageSharp/Common/Helpers/ColorNumerics.cs
new file mode 100644
index 0000000000..6f225b1109
--- /dev/null
+++ b/src/ImageSharp/Common/Helpers/ColorNumerics.cs
@@ -0,0 +1,177 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace SixLabors.ImageSharp
+{
+ ///
+ /// Provides optimized static methods for common mathematical functions specific
+ /// to color processing.
+ ///
+ internal static class ColorNumerics
+ {
+ ///
+ /// Vector for converting pixel to gray value as specified by
+ /// ITU-R Recommendation BT.709.
+ ///
+ private static readonly Vector4 Bt709 = new Vector4(.2126f, .7152f, .0722f, 0.0f);
+
+ ///
+ /// Convert a pixel value to grayscale using ITU-R Recommendation BT.709.
+ ///
+ /// The vector to get the luminance from.
+ ///
+ /// The number of luminance levels (256 for 8 bit, 65536 for 16 bit grayscale images).
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int GetBT709Luminance(ref Vector4 vector, int luminanceLevels)
+ => (int)MathF.Round(Vector4.Dot(vector, Bt709) * (luminanceLevels - 1));
+
+ ///
+ /// Gets the luminance from the rgb components using the formula
+ /// as specified by ITU-R Recommendation BT.709.
+ ///
+ /// The red component.
+ /// The green component.
+ /// The blue component.
+ /// The .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static byte Get8BitBT709Luminance(byte r, byte g, byte b)
+ => (byte)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F);
+
+ ///
+ /// Gets the luminance from the rgb components using the formula as
+ /// specified by ITU-R Recommendation BT.709.
+ ///
+ /// The red component.
+ /// The green component.
+ /// The blue component.
+ /// The .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ushort Get16BitBT709Luminance(ushort r, ushort g, ushort b)
+ => (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F);
+
+ ///
+ /// Gets the luminance from the rgb components using the formula as specified
+ /// by ITU-R Recommendation BT.709.
+ ///
+ /// The red component.
+ /// The green component.
+ /// The blue component.
+ /// The .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ushort Get16BitBT709Luminance(float r, float g, float b)
+ => (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F);
+
+ ///
+ /// Scales a value from a 16 bit to an
+ /// 8 bit equivalent.
+ ///
+ /// The 8 bit component value.
+ /// The
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static byte DownScaleFrom16BitTo8Bit(ushort component)
+ {
+ // To scale to 8 bits From a 16-bit value V the required value (from the PNG specification) is:
+ //
+ // (V * 255) / 65535
+ //
+ // This reduces to round(V / 257), or floor((V + 128.5)/257)
+ //
+ // Represent V as the two byte value vhi.vlo. Make a guess that the
+ // result is the top byte of V, vhi, then the correction to this value
+ // is:
+ //
+ // error = floor(((V-vhi.vhi) + 128.5) / 257)
+ // = floor(((vlo-vhi) + 128.5) / 257)
+ //
+ // This can be approximated using integer arithmetic (and a signed
+ // shift):
+ //
+ // error = (vlo-vhi+128) >> 8;
+ //
+ // The approximate differs from the exact answer only when (vlo-vhi) is
+ // 128; it then gives a correction of +1 when the exact correction is
+ // 0. This gives 128 errors. The exact answer (correct for all 16-bit
+ // input values) is:
+ //
+ // error = (vlo-vhi+128)*65535 >> 24;
+ //
+ // An alternative arithmetic calculation which also gives no errors is:
+ //
+ // (V * 255 + 32895) >> 16
+ return (byte)(((component * 255) + 32895) >> 16);
+ }
+
+ ///
+ /// Scales a value from an 8 bit to
+ /// an 16 bit equivalent.
+ ///
+ /// The 8 bit component value.
+ /// The
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ushort UpscaleFrom8BitTo16Bit(byte component)
+ => (ushort)(component * 257);
+
+ ///
+ /// Returns how many bits are required to store the specified number of colors.
+ /// Performs a Log2() on the value.
+ ///
+ /// The number of colors.
+ ///
+ /// The
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int GetBitsNeededForColorDepth(int colors)
+ => Math.Max(1, (int)Math.Ceiling(Math.Log(colors, 2)));
+
+ ///
+ /// Returns how many colors will be created by the specified number of bits.
+ ///
+ /// The bit depth.
+ /// The
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int GetColorCountForBitDepth(int bitDepth)
+ => 1 << bitDepth;
+
+ ///
+ /// Transforms a vector by the given color matrix.
+ ///
+ /// The source vector.
+ /// The transformation color matrix.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void Transform(ref Vector4 vector, ref ColorMatrix matrix)
+ {
+ float x = vector.X;
+ float y = vector.Y;
+ float z = vector.Z;
+ float w = vector.W;
+
+ vector.X = (x * matrix.M11) + (y * matrix.M21) + (z * matrix.M31) + (w * matrix.M41) + matrix.M51;
+ vector.Y = (x * matrix.M12) + (y * matrix.M22) + (z * matrix.M32) + (w * matrix.M42) + matrix.M52;
+ vector.Z = (x * matrix.M13) + (y * matrix.M23) + (z * matrix.M33) + (w * matrix.M43) + matrix.M53;
+ vector.W = (x * matrix.M14) + (y * matrix.M24) + (z * matrix.M34) + (w * matrix.M44) + matrix.M54;
+ }
+
+ ///
+ /// Bulk variant of .
+ ///
+ /// The span of vectors
+ /// The transformation color matrix.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void Transform(Span vectors, ref ColorMatrix matrix)
+ {
+ ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors);
+
+ for (int i = 0; i < vectors.Length; i++)
+ {
+ ref Vector4 v = ref Unsafe.Add(ref baseRef, i);
+ Transform(ref v, ref matrix);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs b/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs
deleted file mode 100644
index 61f90e23e1..0000000000
--- a/src/ImageSharp/Common/Helpers/DenseMatrixUtils.cs
+++ /dev/null
@@ -1,279 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
-
-using System;
-using System.Numerics;
-using System.Runtime.CompilerServices;
-using SixLabors.ImageSharp.Memory;
-using SixLabors.ImageSharp.PixelFormats;
-
-namespace SixLabors.ImageSharp
-{
- ///
- /// Extension methods for .
- /// TODO: One day rewrite all this to use SIMD intrinsics. There's a lot of scope for improvement.
- ///
- internal static class DenseMatrixUtils
- {
- ///
- /// Computes the sum of vectors in the span referenced by weighted by the two kernel weight values.
- /// Using this method the convolution filter is not applied to alpha in addition to the color channels.
- ///
- /// The pixel format.
- /// The vertical dense matrix.
- /// The horizontal dense matrix.
- /// The source frame.
- /// The target row base reference.
- /// The current row.
- /// The current column.
- /// The minimum working area row.
- /// The maximum working area row.
- /// The minimum working area column.
- /// The maximum working area column.
- [MethodImpl(InliningOptions.ShortMethod)]
- public static void Convolve2D3(
- in DenseMatrix matrixY,
- in DenseMatrix matrixX,
- Buffer2D sourcePixels,
- ref Vector4 targetRowRef,
- int row,
- int column,
- int minRow,
- int maxRow,
- int minColumn,
- int maxColumn)
- where TPixel : unmanaged, IPixel
- {
- Convolve2DImpl(
- in matrixY,
- in matrixX,
- sourcePixels,
- row,
- column,
- minRow,
- maxRow,
- minColumn,
- maxColumn,
- out Vector4 vector);
-
- ref Vector4 target = ref Unsafe.Add(ref targetRowRef, column);
- vector.W = target.W;
-
- Vector4Utilities.UnPremultiply(ref vector);
- target = vector;
- }
-
- ///
- /// Computes the sum of vectors in the span referenced by weighted by the two kernel weight values.
- /// Using this method the convolution filter is applied to alpha in addition to the color channels.
- ///
- /// The pixel format.
- /// The vertical dense matrix.
- /// The horizontal dense matrix.
- /// The source frame.
- /// The target row base reference.
- /// The current row.
- /// The current column.
- /// The minimum working area row.
- /// The maximum working area row.
- /// The minimum working area column.
- /// The maximum working area column.
- [MethodImpl(InliningOptions.ShortMethod)]
- public static void Convolve2D4(
- in DenseMatrix matrixY,
- in DenseMatrix matrixX,
- Buffer2D sourcePixels,
- ref Vector4 targetRowRef,
- int row,
- int column,
- int minRow,
- int maxRow,
- int minColumn,
- int maxColumn)
- where TPixel : unmanaged, IPixel
- {
- Convolve2DImpl(
- in matrixY,
- in matrixX,
- sourcePixels,
- row,
- column,
- minRow,
- maxRow,
- minColumn,
- maxColumn,
- out Vector4 vector);
-
- ref Vector4 target = ref Unsafe.Add(ref targetRowRef, column);
- Vector4Utilities.UnPremultiply(ref vector);
- target = vector;
- }
-
- [MethodImpl(InliningOptions.ShortMethod)]
- public static void Convolve2DImpl(
- in DenseMatrix matrixY,
- in DenseMatrix matrixX,
- Buffer2D sourcePixels,
- int row,
- int column,
- int minRow,
- int maxRow,
- int minColumn,
- int maxColumn,
- out Vector4 vector)
- where TPixel : unmanaged, IPixel
- {
- Vector4 vectorY = default;
- Vector4 vectorX = default;
- int matrixHeight = matrixY.Rows;
- int matrixWidth = matrixY.Columns;
- int radiusY = matrixHeight >> 1;
- int radiusX = matrixWidth >> 1;
- int sourceOffsetColumnBase = column + minColumn;
-
- for (int y = 0; y < matrixHeight; y++)
- {
- int offsetY = (row + y - radiusY).Clamp(minRow, maxRow);
- Span sourceRowSpan = sourcePixels.GetRowSpan(offsetY);
-
- for (int x = 0; x < matrixWidth; x++)
- {
- int offsetX = (sourceOffsetColumnBase + x - radiusX).Clamp(minColumn, maxColumn);
- var currentColor = sourceRowSpan[offsetX].ToVector4();
- Vector4Utilities.Premultiply(ref currentColor);
-
- vectorX += matrixX[y, x] * currentColor;
- vectorY += matrixY[y, x] * currentColor;
- }
- }
-
- vector = Vector4.SquareRoot((vectorX * vectorX) + (vectorY * vectorY));
- }
-
- ///
- /// Computes the sum of vectors in the span referenced by weighted by the kernel weight values.
- /// Using this method the convolution filter is not applied to alpha in addition to the color channels.
- ///
- /// The pixel format.
- /// The dense matrix.
- /// The source frame.
- /// The target row base reference.
- /// The current row.
- /// The current column.
- /// The minimum working area row.
- /// The maximum working area row.
- /// The minimum working area column.
- /// The maximum working area column.
- [MethodImpl(InliningOptions.ShortMethod)]
- public static void Convolve3(
- in DenseMatrix matrix,
- Buffer2D sourcePixels,
- ref Vector4 targetRowRef,
- int row,
- int column,
- int minRow,
- int maxRow,
- int minColumn,
- int maxColumn)
- where TPixel : unmanaged, IPixel
- {
- Vector4 vector = default;
-
- ConvolveImpl(
- in matrix,
- sourcePixels,
- row,
- column,
- minRow,
- maxRow,
- minColumn,
- maxColumn,
- ref vector);
-
- ref Vector4 target = ref Unsafe.Add(ref targetRowRef, column);
- vector.W = target.W;
-
- Vector4Utilities.UnPremultiply(ref vector);
- target = vector;
- }
-
- ///
- /// Computes the sum of vectors in the span referenced by weighted by the kernel weight values.
- /// Using this method the convolution filter is applied to alpha in addition to the color channels.
- ///
- /// The pixel format.
- /// The dense matrix.
- /// The source frame.
- /// The target row base reference.
- /// The current row.
- /// The current column.
- /// The minimum working area row.
- /// The maximum working area row.
- /// The minimum working area column.
- /// The maximum working area column.
- [MethodImpl(InliningOptions.ShortMethod)]
- public static void Convolve4(
- in DenseMatrix matrix,
- Buffer2D sourcePixels,
- ref Vector4 targetRowRef,
- int row,
- int column,
- int minRow,
- int maxRow,
- int minColumn,
- int maxColumn)
- where TPixel : unmanaged, IPixel
- {
- Vector4 vector = default;
-
- ConvolveImpl(
- in matrix,
- sourcePixels,
- row,
- column,
- minRow,
- maxRow,
- minColumn,
- maxColumn,
- ref vector);
-
- ref Vector4 target = ref Unsafe.Add(ref targetRowRef, column);
- Vector4Utilities.UnPremultiply(ref vector);
- target = vector;
- }
-
- [MethodImpl(InliningOptions.ShortMethod)]
- private static void ConvolveImpl(
- in DenseMatrix matrix,
- Buffer2D sourcePixels,
- int row,
- int column,
- int minRow,
- int maxRow,
- int minColumn,
- int maxColumn,
- ref Vector4 vector)
- where TPixel : unmanaged, IPixel
- {
- int matrixHeight = matrix.Rows;
- int matrixWidth = matrix.Columns;
- int radiusY = matrixHeight >> 1;
- int radiusX = matrixWidth >> 1;
- int sourceOffsetColumnBase = column + minColumn;
-
- for (int y = 0; y < matrixHeight; y++)
- {
- int offsetY = (row + y - radiusY).Clamp(minRow, maxRow);
- Span sourceRowSpan = sourcePixels.GetRowSpan(offsetY);
-
- for (int x = 0; x < matrixWidth; x++)
- {
- int offsetX = (sourceOffsetColumnBase + x - radiusX).Clamp(minColumn, maxColumn);
- var currentColor = sourceRowSpan[offsetX].ToVector4();
- Vector4Utilities.Premultiply(ref currentColor);
- vector += matrix[y, x] * currentColor;
- }
- }
- }
- }
-}
diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs
deleted file mode 100644
index 977432f8bb..0000000000
--- a/src/ImageSharp/Common/Helpers/ImageMaths.cs
+++ /dev/null
@@ -1,373 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
-
-using System;
-using System.Numerics;
-using System.Runtime.CompilerServices;
-
-using SixLabors.ImageSharp.PixelFormats;
-
-namespace SixLabors.ImageSharp
-{
- ///
- /// Provides common mathematical methods.
- ///
- internal static class ImageMaths
- {
- ///
- /// Vector for converting pixel to gray value as specified by ITU-R Recommendation BT.709.
- ///
- private static readonly Vector4 Bt709 = new Vector4(.2126f, .7152f, .0722f, 0.0f);
-
- ///
- /// Convert a pixel value to grayscale using ITU-R Recommendation BT.709.
- ///
- /// The vector to get the luminance from.
- /// The number of luminance levels (256 for 8 bit, 65536 for 16 bit grayscale images)
- [MethodImpl(InliningOptions.ShortMethod)]
- public static int GetBT709Luminance(ref Vector4 vector, int luminanceLevels)
- => (int)MathF.Round(Vector4.Dot(vector, Bt709) * (luminanceLevels - 1));
-
- ///
- /// Gets the luminance from the rgb components using the formula as specified by ITU-R Recommendation BT.709.
- ///
- /// The red component.
- /// The green component.
- /// The blue component.
- /// The .
- [MethodImpl(InliningOptions.ShortMethod)]
- public static byte Get8BitBT709Luminance(byte r, byte g, byte b) =>
- (byte)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F);
-
- ///
- /// Gets the luminance from the rgb components using the formula as specified by ITU-R Recommendation BT.709.
- ///
- /// The red component.
- /// The green component.
- /// The blue component.
- /// The .
- [MethodImpl(InliningOptions.ShortMethod)]
- public static ushort Get16BitBT709Luminance(ushort r, ushort g, ushort b) =>
- (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F);
-
- ///
- /// Gets the luminance from the rgb components using the formula as specified by ITU-R Recommendation BT.709.
- ///
- /// The red component.
- /// The green component.
- /// The blue component.
- /// The .
- [MethodImpl(InliningOptions.ShortMethod)]
- public static ushort Get16BitBT709Luminance(float r, float g, float b) =>
- (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F);
-
- ///
- /// Scales a value from a 16 bit to it's 8 bit equivalent.
- ///
- /// The 8 bit component value.
- /// The
- [MethodImpl(InliningOptions.ShortMethod)]
- public static byte DownScaleFrom16BitTo8Bit(ushort component)
- {
- // To scale to 8 bits From a 16-bit value V the required value (from the PNG specification) is:
- //
- // (V * 255) / 65535
- //
- // This reduces to round(V / 257), or floor((V + 128.5)/257)
- //
- // Represent V as the two byte value vhi.vlo. Make a guess that the
- // result is the top byte of V, vhi, then the correction to this value
- // is:
- //
- // error = floor(((V-vhi.vhi) + 128.5) / 257)
- // = floor(((vlo-vhi) + 128.5) / 257)
- //
- // This can be approximated using integer arithmetic (and a signed
- // shift):
- //
- // error = (vlo-vhi+128) >> 8;
- //
- // The approximate differs from the exact answer only when (vlo-vhi) is
- // 128; it then gives a correction of +1 when the exact correction is
- // 0. This gives 128 errors. The exact answer (correct for all 16-bit
- // input values) is:
- //
- // error = (vlo-vhi+128)*65535 >> 24;
- //
- // An alternative arithmetic calculation which also gives no errors is:
- //
- // (V * 255 + 32895) >> 16
- return (byte)(((component * 255) + 32895) >> 16);
- }
-
- ///
- /// Scales a value from an 8 bit to it's 16 bit equivalent.
- ///
- /// The 8 bit component value.
- /// The
- [MethodImpl(InliningOptions.ShortMethod)]
- public static ushort UpscaleFrom8BitTo16Bit(byte component) => (ushort)(component * 257);
-
- ///
- /// Determine the Greatest CommonDivisor (GCD) of two numbers.
- ///
- public static int GreatestCommonDivisor(int a, int b)
- {
- while (b != 0)
- {
- int temp = b;
- b = a % b;
- a = temp;
- }
-
- return a;
- }
-
- ///
- /// Determine the Least Common Multiple (LCM) of two numbers.
- ///
- public static int LeastCommonMultiple(int a, int b)
- {
- // https://en.wikipedia.org/wiki/Least_common_multiple#Reduction_by_the_greatest_common_divisor
- return (a / GreatestCommonDivisor(a, b)) * b;
- }
-
- ///
- /// Calculates % 4
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public static int Modulo4(int x) => x & 3;
-
- ///
- /// Calculates % 8
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public static int Modulo8(int x) => x & 7;
-
- ///
- /// Fast (x mod m) calculator, with the restriction that
- /// should be power of 2.
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public static int ModuloP2(int x, int m) => x & (m - 1);
-
- ///
- /// Returns the absolute value of a 32-bit signed integer. Uses bit shifting to speed up the operation.
- ///
- ///
- /// A number that is greater than , but less than or equal to
- ///
- /// The
- [MethodImpl(InliningOptions.ShortMethod)]
- public static int FastAbs(int x)
- {
- int y = x >> 31;
- return (x ^ y) - y;
- }
-
- ///
- /// Returns a specified number raised to the power of 2
- ///
- /// A single-precision floating-point number
- /// The number raised to the power of 2.
- [MethodImpl(InliningOptions.ShortMethod)]
- public static float Pow2(float x) => x * x;
-
- ///
- /// Returns a specified number raised to the power of 3
- ///
- /// A single-precision floating-point number
- /// The number raised to the power of 3.
- [MethodImpl(InliningOptions.ShortMethod)]
- public static float Pow3(float x) => x * x * x;
-
- ///
- /// Returns how many bits are required to store the specified number of colors.
- /// Performs a Log2() on the value.
- ///
- /// The number of colors.
- ///
- /// The
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public static int GetBitsNeededForColorDepth(int colors) => Math.Max(1, (int)Math.Ceiling(Math.Log(colors, 2)));
-
- ///
- /// Returns how many colors will be created by the specified number of bits.
- ///
- /// The bit depth.
- /// The
- [MethodImpl(InliningOptions.ShortMethod)]
- public static int GetColorCountForBitDepth(int bitDepth) => 1 << bitDepth;
-
- ///
- /// Implementation of 1D Gaussian G(x) function
- ///
- /// The x provided to G(x).
- /// The spread of the blur.
- /// The Gaussian G(x)
- [MethodImpl(InliningOptions.ShortMethod)]
- public static float Gaussian(float x, float sigma)
- {
- const float Numerator = 1.0f;
- float denominator = MathF.Sqrt(2 * MathF.PI) * sigma;
-
- float exponentNumerator = -x * x;
- float exponentDenominator = 2 * Pow2(sigma);
-
- float left = Numerator / denominator;
- float right = MathF.Exp(exponentNumerator / exponentDenominator);
-
- return left * right;
- }
-
- ///
- /// Returns the result of a normalized sine cardinal function for the given value.
- /// SinC(x) = sin(pi*x)/(pi*x).
- ///
- /// A single-precision floating-point number to calculate the result for.
- ///
- /// The sine cardinal of .
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public static float SinC(float f)
- {
- if (MathF.Abs(f) > Constants.Epsilon)
- {
- f *= MathF.PI;
- float result = MathF.Sin(f) / f;
- return MathF.Abs(result) < Constants.Epsilon ? 0F : result;
- }
-
- return 1F;
- }
-
- ///
- /// Gets the bounding from the given points.
- ///
- ///
- /// The designating the top left position.
- ///
- ///
- /// The designating the bottom right position.
- ///
- ///
- /// The bounding .
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- public static Rectangle GetBoundingRectangle(Point topLeft, Point bottomRight) => new Rectangle(topLeft.X, topLeft.Y, bottomRight.X - topLeft.X, bottomRight.Y - topLeft.Y);
-
- ///
- /// Finds the bounding rectangle based on the first instance of any color component other
- /// than the given one.
- ///
- /// The pixel format.
- /// The to search within.
- /// The color component value to remove.
- /// The channel to test against.
- ///
- /// The .
- ///
- public static Rectangle GetFilteredBoundingRectangle(ImageFrame bitmap, float componentValue, RgbaComponent channel = RgbaComponent.B)
- where TPixel : unmanaged, IPixel
- {
- int width = bitmap.Width;
- int height = bitmap.Height;
- Point topLeft = default;
- Point bottomRight = default;
-
- Func, int, int, float, bool> delegateFunc;
-
- // Determine which channel to check against
- switch (channel)
- {
- case RgbaComponent.R:
- delegateFunc = (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToVector4().X - b) > Constants.Epsilon;
- break;
-
- case RgbaComponent.G:
- delegateFunc = (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToVector4().Y - b) > Constants.Epsilon;
- break;
-
- case RgbaComponent.B:
- delegateFunc = (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToVector4().Z - b) > Constants.Epsilon;
- break;
-
- default:
- delegateFunc = (pixels, x, y, b) => MathF.Abs(pixels[x, y].ToVector4().W - b) > Constants.Epsilon;
- break;
- }
-
- int GetMinY(ImageFrame pixels)
- {
- for (int y = 0; y < height; y++)
- {
- for (int x = 0; x < width; x++)
- {
- if (delegateFunc(pixels, x, y, componentValue))
- {
- return y;
- }
- }
- }
-
- return 0;
- }
-
- int GetMaxY(ImageFrame pixels)
- {
- for (int y = height - 1; y > -1; y--)
- {
- for (int x = 0; x < width; x++)
- {
- if (delegateFunc(pixels, x, y, componentValue))
- {
- return y;
- }
- }
- }
-
- return height;
- }
-
- int GetMinX(ImageFrame pixels)
- {
- for (int x = 0; x < width; x++)
- {
- for (int y = 0; y < height; y++)
- {
- if (delegateFunc(pixels, x, y, componentValue))
- {
- return x;
- }
- }
- }
-
- return 0;
- }
-
- int GetMaxX(ImageFrame pixels)
- {
- for (int x = width - 1; x > -1; x--)
- {
- for (int y = 0; y < height; y++)
- {
- if (delegateFunc(pixels, x, y, componentValue))
- {
- return x;
- }
- }
- }
-
- return width;
- }
-
- topLeft.Y = GetMinY(bitmap);
- topLeft.X = GetMinX(bitmap);
- bottomRight.Y = (GetMaxY(bitmap) + 1).Clamp(0, height);
- bottomRight.X = (GetMaxX(bitmap) + 1).Clamp(0, width);
-
- return GetBoundingRectangle(topLeft, bottomRight);
- }
- }
-}
diff --git a/src/ImageSharp/Common/Helpers/Numerics.cs b/src/ImageSharp/Common/Helpers/Numerics.cs
new file mode 100644
index 0000000000..6105422372
--- /dev/null
+++ b/src/ImageSharp/Common/Helpers/Numerics.cs
@@ -0,0 +1,752 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Numerics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+#if SUPPORTS_RUNTIME_INTRINSICS
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+#endif
+
+namespace SixLabors.ImageSharp
+{
+ ///
+ /// Provides optimized static methods for trigonometric, logarithmic,
+ /// and other common mathematical functions.
+ ///
+ internal static class Numerics
+ {
+#if SUPPORTS_RUNTIME_INTRINSICS
+ public const int BlendAlphaControl = 0b_10_00_10_00;
+ private const int ShuffleAlphaControl = 0b_11_11_11_11;
+#endif
+
+ ///
+ /// Determine the Greatest CommonDivisor (GCD) of two numbers.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int GreatestCommonDivisor(int a, int b)
+ {
+ while (b != 0)
+ {
+ int temp = b;
+ b = a % b;
+ a = temp;
+ }
+
+ return a;
+ }
+
+ ///
+ /// Determine the Least Common Multiple (LCM) of two numbers.
+ /// See https://en.wikipedia.org/wiki/Least_common_multiple#Reduction_by_the_greatest_common_divisor.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int LeastCommonMultiple(int a, int b)
+ => a / GreatestCommonDivisor(a, b) * b;
+
+ ///
+ /// Calculates % 2
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int Modulo2(int x) => x & 1;
+
+ ///
+ /// Calculates % 4
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int Modulo4(int x) => x & 3;
+
+ ///
+ /// Calculates % 8
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int Modulo8(int x) => x & 7;
+
+ ///
+ /// Fast (x mod m) calculator, with the restriction that
+ /// should be power of 2.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int ModuloP2(int x, int m) => x & (m - 1);
+
+ ///
+ /// Returns the absolute value of a 32-bit signed integer.
+ /// Uses bit shifting to speed up the operation compared to .
+ ///
+ ///
+ /// A number that is greater than , but less than
+ /// or equal to
+ ///
+ /// The
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int Abs(int x)
+ {
+ int y = x >> 31;
+ return (x ^ y) - y;
+ }
+
+ ///
+ /// Returns a specified number raised to the power of 2
+ ///
+ /// A single-precision floating-point number
+ /// The number raised to the power of 2.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Pow2(float x) => x * x;
+
+ ///
+ /// Returns a specified number raised to the power of 3
+ ///
+ /// A single-precision floating-point number
+ /// The number raised to the power of 3.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Pow3(float x) => x * x * x;
+
+ ///
+ /// Implementation of 1D Gaussian G(x) function
+ ///
+ /// The x provided to G(x).
+ /// The spread of the blur.
+ /// The Gaussian G(x)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Gaussian(float x, float sigma)
+ {
+ const float Numerator = 1.0f;
+ float denominator = MathF.Sqrt(2 * MathF.PI) * sigma;
+
+ float exponentNumerator = -x * x;
+ float exponentDenominator = 2 * Pow2(sigma);
+
+ float left = Numerator / denominator;
+ float right = MathF.Exp(exponentNumerator / exponentDenominator);
+
+ return left * right;
+ }
+
+ ///
+ /// Returns the result of a normalized sine cardinal function for the given value.
+ /// SinC(x) = sin(pi*x)/(pi*x).
+ ///
+ /// A single-precision floating-point number to calculate the result for.
+ ///
+ /// The sine cardinal of .
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float SinC(float f)
+ {
+ if (MathF.Abs(f) > Constants.Epsilon)
+ {
+ f *= MathF.PI;
+ float result = MathF.Sin(f) / f;
+ return MathF.Abs(result) < Constants.Epsilon ? 0F : result;
+ }
+
+ return 1F;
+ }
+
+ ///
+ /// Returns the value clamped to the inclusive range of min and max.
+ ///
+ /// The value to clamp.
+ /// The minimum inclusive value.
+ /// The maximum inclusive value.
+ /// The clamped .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static byte Clamp(byte value, byte min, byte max)
+ {
+ // Order is important here as someone might set min to higher than max.
+ if (value > max)
+ {
+ return max;
+ }
+
+ if (value < min)
+ {
+ return min;
+ }
+
+ return value;
+ }
+
+ ///
+ /// Returns the value clamped to the inclusive range of min and max.
+ ///
+ /// The value to clamp.
+ /// The minimum inclusive value.
+ /// The maximum inclusive value.
+ /// The clamped .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static uint Clamp(uint value, uint min, uint max)
+ {
+ if (value > max)
+ {
+ return max;
+ }
+
+ if (value < min)
+ {
+ return min;
+ }
+
+ return value;
+ }
+
+ ///
+ /// Returns the value clamped to the inclusive range of min and max.
+ ///
+ /// The value to clamp.
+ /// The minimum inclusive value.
+ /// The maximum inclusive value.
+ /// The clamped .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int Clamp(int value, int min, int max)
+ {
+ if (value > max)
+ {
+ return max;
+ }
+
+ if (value < min)
+ {
+ return min;
+ }
+
+ return value;
+ }
+
+ ///
+ /// Returns the value clamped to the inclusive range of min and max.
+ ///
+ /// The value to clamp.
+ /// The minimum inclusive value.
+ /// The maximum inclusive value.
+ /// The clamped .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Clamp(float value, float min, float max)
+ {
+ if (value > max)
+ {
+ return max;
+ }
+
+ if (value < min)
+ {
+ return min;
+ }
+
+ return value;
+ }
+
+ ///
+ /// Returns the value clamped to the inclusive range of min and max.
+ ///
+ /// The value to clamp.
+ /// The minimum inclusive value.
+ /// The maximum inclusive value.
+ /// The clamped .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static double Clamp(double value, double min, double max)
+ {
+ if (value > max)
+ {
+ return max;
+ }
+
+ if (value < min)
+ {
+ return min;
+ }
+
+ return value;
+ }
+
+ ///
+ /// Returns the value clamped to the inclusive range of min and max.
+ /// 5x Faster than
+ /// on platforms < NET 5.
+ ///
+ /// The value to clamp.
+ /// The minimum inclusive value.
+ /// The maximum inclusive value.
+ /// The clamped .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Vector4 Clamp(Vector4 value, Vector4 min, Vector4 max)
+ => Vector4.Min(Vector4.Max(value, min), max);
+
+ ///
+ /// Clamps the span values to the inclusive range of min and max.
+ ///
+ /// The span containing the values to clamp.
+ /// The minimum inclusive value.
+ /// The maximum inclusive value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void Clamp(Span span, byte min, byte max)
+ {
+ Span remainder = span.Slice(ClampReduce(span, min, max));
+
+ if (remainder.Length > 0)
+ {
+ ref byte remainderStart = ref MemoryMarshal.GetReference(remainder);
+ ref byte remainderEnd = ref Unsafe.Add(ref remainderStart, remainder.Length);
+
+ while (Unsafe.IsAddressLessThan(ref remainderStart, ref remainderEnd))
+ {
+ remainderStart = Clamp(remainderStart, min, max);
+
+ remainderStart = ref Unsafe.Add(ref remainderStart, 1);
+ }
+ }
+ }
+
+ ///
+ /// Clamps the span values to the inclusive range of min and max.
+ ///
+ /// The span containing the values to clamp.
+ /// The minimum inclusive value.
+ /// The maximum inclusive value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void Clamp(Span span, uint min, uint max)
+ {
+ Span remainder = span.Slice(ClampReduce(span, min, max));
+
+ if (remainder.Length > 0)
+ {
+ ref uint remainderStart = ref MemoryMarshal.GetReference(remainder);
+ ref uint remainderEnd = ref Unsafe.Add(ref remainderStart, remainder.Length);
+
+ while (Unsafe.IsAddressLessThan(ref remainderStart, ref remainderEnd))
+ {
+ remainderStart = Clamp(remainderStart, min, max);
+
+ remainderStart = ref Unsafe.Add(ref remainderStart, 1);
+ }
+ }
+ }
+
+ ///
+ /// Clamps the span values to the inclusive range of min and max.
+ ///
+ /// The span containing the values to clamp.
+ /// The minimum inclusive value.
+ /// The maximum inclusive value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void Clamp(Span span, int min, int max)
+ {
+ Span remainder = span.Slice(ClampReduce(span, min, max));
+
+ if (remainder.Length > 0)
+ {
+ ref int remainderStart = ref MemoryMarshal.GetReference(remainder);
+ ref int remainderEnd = ref Unsafe.Add(ref remainderStart, remainder.Length);
+
+ while (Unsafe.IsAddressLessThan(ref remainderStart, ref remainderEnd))
+ {
+ remainderStart = Clamp(remainderStart, min, max);
+
+ remainderStart = ref Unsafe.Add(ref remainderStart, 1);
+ }
+ }
+ }
+
+ ///
+ /// Clamps the span values to the inclusive range of min and max.
+ ///
+ /// The span containing the values to clamp.
+ /// The minimum inclusive value.
+ /// The maximum inclusive value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void Clamp(Span span, float min, float max)
+ {
+ Span remainder = span.Slice(ClampReduce(span, min, max));
+
+ if (remainder.Length > 0)
+ {
+ ref float remainderStart = ref MemoryMarshal.GetReference(remainder);
+ ref float remainderEnd = ref Unsafe.Add(ref remainderStart, remainder.Length);
+
+ while (Unsafe.IsAddressLessThan(ref remainderStart, ref remainderEnd))
+ {
+ remainderStart = Clamp(remainderStart, min, max);
+
+ remainderStart = ref Unsafe.Add(ref remainderStart, 1);
+ }
+ }
+ }
+
+ ///
+ /// Clamps the span values to the inclusive range of min and max.
+ ///
+ /// The span containing the values to clamp.
+ /// The minimum inclusive value.
+ /// The maximum inclusive value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void Clamp(Span span, double min, double max)
+ {
+ Span remainder = span.Slice(ClampReduce(span, min, max));
+
+ if (remainder.Length > 0)
+ {
+ ref double remainderStart = ref MemoryMarshal.GetReference(remainder);
+ ref double remainderEnd = ref Unsafe.Add(ref remainderStart, remainder.Length);
+
+ while (Unsafe.IsAddressLessThan(ref remainderStart, ref remainderEnd))
+ {
+ remainderStart = Clamp(remainderStart, min, max);
+
+ remainderStart = ref Unsafe.Add(ref remainderStart, 1);
+ }
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int ClampReduce(Span span, T min, T max)
+ where T : unmanaged
+ {
+ if (Vector.IsHardwareAccelerated && span.Length >= Vector.Count)
+ {
+ int remainder = ModuloP2(span.Length, Vector.Count);
+ int adjustedCount = span.Length - remainder;
+
+ if (adjustedCount > 0)
+ {
+ ClampImpl(span.Slice(0, adjustedCount), min, max);
+ }
+
+ return adjustedCount;
+ }
+
+ return 0;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void ClampImpl(Span span, T min, T max)
+ where T : unmanaged
+ {
+ ref T sRef = ref MemoryMarshal.GetReference(span);
+ var vmin = new Vector(min);
+ var vmax = new Vector(max);
+
+ int n = span.Length / Vector.Count;
+ int m = Modulo4(n);
+ int u = n - m;
+
+ ref Vector vs0 = ref Unsafe.As>(ref MemoryMarshal.GetReference(span));
+ ref Vector vs1 = ref Unsafe.Add(ref vs0, 1);
+ ref Vector vs2 = ref Unsafe.Add(ref vs0, 2);
+ ref Vector vs3 = ref Unsafe.Add(ref vs0, 3);
+ ref Vector vsEnd = ref Unsafe.Add(ref vs0, u);
+
+ while (Unsafe.IsAddressLessThan(ref vs0, ref vsEnd))
+ {
+ vs0 = Vector.Min(Vector.Max(vmin, vs0), vmax);
+ vs1 = Vector.Min(Vector.Max(vmin, vs1), vmax);
+ vs2 = Vector.Min(Vector.Max(vmin, vs2), vmax);
+ vs3 = Vector.Min(Vector.Max(vmin, vs3), vmax);
+
+ vs0 = ref Unsafe.Add(ref vs0, 4);
+ vs1 = ref Unsafe.Add(ref vs1, 4);
+ vs2 = ref Unsafe.Add(ref vs2, 4);
+ vs3 = ref Unsafe.Add(ref vs3, 4);
+ }
+
+ if (m > 0)
+ {
+ vs0 = ref vsEnd;
+ vsEnd = ref Unsafe.Add(ref vsEnd, m);
+
+ while (Unsafe.IsAddressLessThan(ref vs0, ref vsEnd))
+ {
+ vs0 = Vector.Min(Vector.Max(vmin, vs0), vmax);
+
+ vs0 = ref Unsafe.Add(ref vs0, 1);
+ }
+ }
+ }
+
+ ///
+ /// Pre-multiplies the "x", "y", "z" components of a vector by its "w" component leaving the "w" component intact.
+ ///
+ /// The to premultiply
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void Premultiply(ref Vector4 source)
+ {
+ float w = source.W;
+ source *= w;
+ source.W = w;
+ }
+
+ ///
+ /// Reverses the result of premultiplying a vector via .
+ ///
+ /// The to premultiply
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void UnPremultiply(ref Vector4 source)
+ {
+ float w = source.W;
+ source /= w;
+ source.W = w;
+ }
+
+ ///
+ /// Bulk variant of
+ ///
+ /// The span of vectors
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void Premultiply(Span vectors)
+ {
+#if SUPPORTS_RUNTIME_INTRINSICS
+ if (Avx2.IsSupported && vectors.Length >= 2)
+ {
+ // Divide by 2 as 4 elements per Vector4 and 8 per Vector256
+ ref Vector256 vectorsBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(vectors));
+ ref Vector256 vectorsLast = ref Unsafe.Add(ref vectorsBase, (IntPtr)((uint)vectors.Length / 2u));
+
+ while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast))
+ {
+ Vector256 source = vectorsBase;
+ Vector256 multiply = Avx.Shuffle(source, source, ShuffleAlphaControl);
+ vectorsBase = Avx.Blend(Avx.Multiply(source, multiply), source, BlendAlphaControl);
+ vectorsBase = ref Unsafe.Add(ref vectorsBase, 1);
+ }
+
+ if (Modulo2(vectors.Length) != 0)
+ {
+ // Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
+ Premultiply(ref MemoryMarshal.GetReference(vectors.Slice(vectors.Length - 1)));
+ }
+ }
+ else
+#endif
+ {
+ ref Vector4 vectorsStart = ref MemoryMarshal.GetReference(vectors);
+ ref Vector4 vectorsEnd = ref Unsafe.Add(ref vectorsStart, vectors.Length);
+
+ while (Unsafe.IsAddressLessThan(ref vectorsStart, ref vectorsEnd))
+ {
+ Premultiply(ref vectorsStart);
+
+ vectorsStart = ref Unsafe.Add(ref vectorsStart, 1);
+ }
+ }
+ }
+
+ ///
+ /// Bulk variant of
+ ///
+ /// The span of vectors
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void UnPremultiply(Span vectors)
+ {
+#if SUPPORTS_RUNTIME_INTRINSICS
+ if (Avx2.IsSupported && vectors.Length >= 2)
+ {
+ // Divide by 2 as 4 elements per Vector4 and 8 per Vector256
+ ref Vector256 vectorsBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(vectors));
+ ref Vector256 vectorsLast = ref Unsafe.Add(ref vectorsBase, (IntPtr)((uint)vectors.Length / 2u));
+
+ while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast))
+ {
+ Vector256 source = vectorsBase;
+ Vector256 multiply = Avx.Shuffle(source, source, ShuffleAlphaControl);
+ vectorsBase = Avx.Blend(Avx.Divide(source, multiply), source, BlendAlphaControl);
+ vectorsBase = ref Unsafe.Add(ref vectorsBase, 1);
+ }
+
+ if (Modulo2(vectors.Length) != 0)
+ {
+ // Vector4 fits neatly in pairs. Any overlap has to be equal to 1.
+ UnPremultiply(ref MemoryMarshal.GetReference(vectors.Slice(vectors.Length - 1)));
+ }
+ }
+ else
+#endif
+ {
+ ref Vector4 vectorsStart = ref MemoryMarshal.GetReference(vectors);
+ ref Vector4 vectorsEnd = ref Unsafe.Add(ref vectorsStart, vectors.Length);
+
+ while (Unsafe.IsAddressLessThan(ref vectorsStart, ref vectorsEnd))
+ {
+ UnPremultiply(ref vectorsStart);
+
+ vectorsStart = ref Unsafe.Add(ref vectorsStart, 1);
+ }
+ }
+ }
+
+ ///
+ /// Calculates the cube pow of all the XYZ channels of the input vectors.
+ ///
+ /// The span of vectors
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe void CubePowOnXYZ(Span vectors)
+ {
+ ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors);
+ ref Vector4 endRef = ref Unsafe.Add(ref baseRef, vectors.Length);
+
+ while (Unsafe.IsAddressLessThan(ref baseRef, ref endRef))
+ {
+ Vector4 v = baseRef;
+ float a = v.W;
+
+ // Fast path for the default gamma exposure, which is 3. In this case we can skip
+ // calling Math.Pow 3 times (one per component), as the method is an internal call and
+ // introduces quite a bit of overhead. Instead, we can just manually multiply the whole
+ // pixel in Vector4 format 3 times, and then restore the alpha channel before copying it
+ // back to the target index in the temporary span. The whole iteration will get completely
+ // inlined and traslated into vectorized instructions, with much better performance.
+ v = v * v * v;
+ v.W = a;
+
+ baseRef = v;
+ baseRef = ref Unsafe.Add(ref baseRef, 1);
+ }
+ }
+
+ ///
+ /// Calculates the cube root of all the XYZ channels of the input vectors.
+ ///
+ /// The span of vectors
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe void CubeRootOnXYZ(Span vectors)
+ {
+#if SUPPORTS_RUNTIME_INTRINSICS
+ if (Sse41.IsSupported)
+ {
+ ref Vector128 vectors128Ref = ref Unsafe.As>(ref MemoryMarshal.GetReference(vectors));
+ ref Vector128 vectors128End = ref Unsafe.Add(ref vectors128Ref, vectors.Length);
+
+ var v128_341 = Vector128.Create(341);
+ Vector128 v128_negativeZero = Vector128.Create(-0.0f).AsInt32();
+ Vector128 v128_one = Vector128.Create(1.0f).AsInt32();
+
+ var v128_13rd = Vector128.Create(1 / 3f);
+ var v128_23rds = Vector128.Create(2 / 3f);
+
+ while (Unsafe.IsAddressLessThan(ref vectors128Ref, ref vectors128End))
+ {
+ Vector128 vecx = vectors128Ref;
+ Vector128 veax = vecx.AsInt32();
+
+ // If we can use SSE41 instructions, we can vectorize the entire cube root calculation, and also execute it
+ // directly on 32 bit floating point values. What follows is a vectorized implementation of this method:
+ // https://www.musicdsp.org/en/latest/Other/206-fast-cube-root-square-root-and-reciprocal-for-x86-sse-cpus.html.
+ // Furthermore, after the initial setup in vectorized form, we're doing two Newton approximations here
+ // using a different succession (the same used below), which should be less unstable due to not having cube pow.
+ veax = Sse2.AndNot(v128_negativeZero, veax);
+ veax = Sse2.Subtract(veax, v128_one);
+ veax = Sse2.ShiftRightArithmetic(veax, 10);
+ veax = Sse41.MultiplyLow(veax, v128_341);
+ veax = Sse2.Add(veax, v128_one);
+ veax = Sse2.AndNot(v128_negativeZero, veax);
+ veax = Sse2.Or(veax, Sse2.And(vecx.AsInt32(), v128_negativeZero));
+
+ Vector128 y4 = veax.AsSingle();
+
+ if (Fma.IsSupported)
+ {
+ y4 = Fma.MultiplyAdd(v128_23rds, y4, Sse.Multiply(v128_13rd, Sse.Divide(vecx, Sse.Multiply(y4, y4))));
+ y4 = Fma.MultiplyAdd(v128_23rds, y4, Sse.Multiply(v128_13rd, Sse.Divide(vecx, Sse.Multiply(y4, y4))));
+ }
+ else
+ {
+ y4 = Sse.Add(Sse.Multiply(v128_23rds, y4), Sse.Multiply(v128_13rd, Sse.Divide(vecx, Sse.Multiply(y4, y4))));
+ y4 = Sse.Add(Sse.Multiply(v128_23rds, y4), Sse.Multiply(v128_13rd, Sse.Divide(vecx, Sse.Multiply(y4, y4))));
+ }
+
+ y4 = Sse41.Insert(y4, vecx, 0xF0);
+
+ vectors128Ref = y4;
+ vectors128Ref = ref Unsafe.Add(ref vectors128Ref, 1);
+ }
+ }
+ else
+#endif
+ {
+ ref Vector4 vectorsRef = ref MemoryMarshal.GetReference(vectors);
+ ref Vector4 vectorsEnd = ref Unsafe.Add(ref vectorsRef, vectors.Length);
+
+ // Fallback with scalar preprocessing and vectorized approximation steps
+ while (Unsafe.IsAddressLessThan(ref vectorsRef, ref vectorsEnd))
+ {
+ Vector4 v = vectorsRef;
+
+ double
+ x64 = v.X,
+ y64 = v.Y,
+ z64 = v.Z;
+ float a = v.W;
+
+ ulong
+ xl = *(ulong*)&x64,
+ yl = *(ulong*)&y64,
+ zl = *(ulong*)&z64;
+
+ // Here we use a trick to compute the starting value x0 for the cube root. This is because doing
+ // pow(x, 1 / gamma) is the same as the gamma-th root of x, and since gamme is 3 in this case,
+ // this means what we actually want is to find the cube root of our clamped values.
+ // For more info on the constant below, see:
+ // https://community.intel.com/t5/Intel-C-Compiler/Fast-approximate-of-transcendental-operations/td-p/1044543.
+ // Here we perform the same trick on all RGB channels separately to help the CPU execute them in paralle, and
+ // store the alpha channel to preserve it. Then we set these values to the fields of a temporary 128-bit
+ // register, and use it to accelerate two steps of the Newton approximation using SIMD.
+ xl = 0x2a9f8a7be393b600 + (xl / 3);
+ yl = 0x2a9f8a7be393b600 + (yl / 3);
+ zl = 0x2a9f8a7be393b600 + (zl / 3);
+
+ Vector4 y4;
+ y4.X = (float)*(double*)&xl;
+ y4.Y = (float)*(double*)&yl;
+ y4.Z = (float)*(double*)&zl;
+ y4.W = 0;
+
+ y4 = (2 / 3f * y4) + (1 / 3f * (v / (y4 * y4)));
+ y4 = (2 / 3f * y4) + (1 / 3f * (v / (y4 * y4)));
+ y4.W = a;
+
+ vectorsRef = y4;
+ vectorsRef = ref Unsafe.Add(ref vectorsRef, 1);
+ }
+ }
+ }
+
+#if SUPPORTS_RUNTIME_INTRINSICS
+
+ ///
+ /// Performs a linear interpolation between two values based on the given weighting.
+ ///
+ /// The first value.
+ /// The second value.
+ /// Values between 0 and 1 that indicates the weight of .
+ /// The .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Vector256 Lerp(
+ in Vector256 value1,
+ in Vector256 value2,
+ in Vector256 amount)
+ {
+ Vector256 diff = Avx.Subtract(value2, value1);
+ if (Fma.IsSupported)
+ {
+ return Fma.MultiplyAdd(diff, amount, value1);
+ }
+ else
+ {
+ return Avx.Add(Avx.Multiply(diff, amount), value1);
+ }
+ }
+#endif
+
+ ///
+ /// Performs a linear interpolation between two values based on the given weighting.
+ ///
+ /// The first value.
+ /// The second value.
+ /// A value between 0 and 1 that indicates the weight of .
+ /// The .
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static float Lerp(float value1, float value2, float amount)
+ => ((value2 - value1) * amount) + value1;
+ }
+}
diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs b/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs
new file mode 100644
index 0000000000..7687a5b95f
--- /dev/null
+++ b/src/ImageSharp/Common/Helpers/Shuffle/IComponentShuffle.cs
@@ -0,0 +1,193 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Buffers.Binary;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// The JIT can detect and optimize rotation idioms ROTL (Rotate Left)
+// and ROTR (Rotate Right) emitting efficient CPU instructions:
+// https://github.com/dotnet/coreclr/pull/1830
+namespace SixLabors.ImageSharp
+{
+ ///
+ /// Defines the contract for methods that allow the shuffling of pixel components.
+ /// Used for shuffling on platforms that do not support Hardware Intrinsics.
+ ///
+ internal interface IComponentShuffle
+ {
+ ///
+ /// Gets the shuffle control.
+ ///
+ byte Control { get; }
+
+ ///
+ /// Shuffle 8-bit integers within 128-bit lanes in
+ /// using the control and store the results in .
+ ///
+ /// The source span of bytes.
+ /// The destination span of bytes.
+ void RunFallbackShuffle(ReadOnlySpan source, Span dest);
+ }
+
+ ///
+ internal interface IShuffle4 : IComponentShuffle
+ {
+ }
+
+ internal readonly struct DefaultShuffle4 : IShuffle4
+ {
+ private readonly byte p3;
+ private readonly byte p2;
+ private readonly byte p1;
+ private readonly byte p0;
+
+ public DefaultShuffle4(byte p3, byte p2, byte p1, byte p0)
+ {
+ DebugGuard.MustBeBetweenOrEqualTo(p3, 0, 3, nameof(p3));
+ DebugGuard.MustBeBetweenOrEqualTo(p2, 0, 3, nameof(p2));
+ DebugGuard.MustBeBetweenOrEqualTo(p1, 0, 3, nameof(p1));
+ DebugGuard.MustBeBetweenOrEqualTo(p0, 0, 3, nameof(p0));
+
+ this.p3 = p3;
+ this.p2 = p2;
+ this.p1 = p1;
+ this.p0 = p0;
+ this.Control = SimdUtils.Shuffle.MmShuffle(p3, p2, p1, p0);
+ }
+
+ public byte Control { get; }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void RunFallbackShuffle(ReadOnlySpan source, Span dest)
+ {
+ ref byte sBase = ref MemoryMarshal.GetReference(source);
+ ref byte dBase = ref MemoryMarshal.GetReference(dest);
+
+ int p3 = this.p3;
+ int p2 = this.p2;
+ int p1 = this.p1;
+ int p0 = this.p0;
+
+ for (int i = 0; i < source.Length; i += 4)
+ {
+ Unsafe.Add(ref dBase, i) = Unsafe.Add(ref sBase, p0 + i);
+ Unsafe.Add(ref dBase, i + 1) = Unsafe.Add(ref sBase, p1 + i);
+ Unsafe.Add(ref dBase, i + 2) = Unsafe.Add(ref sBase, p2 + i);
+ Unsafe.Add(ref dBase, i + 3) = Unsafe.Add(ref sBase, p3 + i);
+ }
+ }
+ }
+
+ internal readonly struct WXYZShuffle4 : IShuffle4
+ {
+ public byte Control
+ {
+ [MethodImpl(InliningOptions.ShortMethod)]
+ get => SimdUtils.Shuffle.MmShuffle(2, 1, 0, 3);
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void RunFallbackShuffle(ReadOnlySpan source, Span dest)
+ {
+ ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source));
+ ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest));
+ int n = source.Length / 4;
+
+ for (int i = 0; i < n; i++)
+ {
+ uint packed = Unsafe.Add(ref sBase, i);
+
+ // packed = [W Z Y X]
+ // ROTL(8, packed) = [Z Y X W]
+ Unsafe.Add(ref dBase, i) = (packed << 8) | (packed >> 24);
+ }
+ }
+ }
+
+ internal readonly struct WZYXShuffle4 : IShuffle4
+ {
+ public byte Control
+ {
+ [MethodImpl(InliningOptions.ShortMethod)]
+ get => SimdUtils.Shuffle.MmShuffle(0, 1, 2, 3);
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void RunFallbackShuffle(ReadOnlySpan source, Span dest)
+ {
+ ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source));
+ ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest));
+ int n = source.Length / 4;
+
+ for (int i = 0; i < n; i++)
+ {
+ uint packed = Unsafe.Add(ref sBase, i);
+
+ // packed = [W Z Y X]
+ // REVERSE(packedArgb) = [X Y Z W]
+ Unsafe.Add(ref dBase, i) = BinaryPrimitives.ReverseEndianness(packed);
+ }
+ }
+ }
+
+ internal readonly struct YZWXShuffle4 : IShuffle4
+ {
+ public byte Control
+ {
+ [MethodImpl(InliningOptions.ShortMethod)]
+ get => SimdUtils.Shuffle.MmShuffle(0, 3, 2, 1);
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void RunFallbackShuffle(ReadOnlySpan source, Span dest)
+ {
+ ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source));
+ ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest));
+ int n = source.Length / 4;
+
+ for (int i = 0; i < n; i++)
+ {
+ uint packed = Unsafe.Add(ref sBase, i);
+
+ // packed = [W Z Y X]
+ // ROTR(8, packedArgb) = [Y Z W X]
+ Unsafe.Add(ref dBase, i) = (packed >> 8) | (packed << 24);
+ }
+ }
+ }
+
+ internal readonly struct ZYXWShuffle4 : IShuffle4
+ {
+ public byte Control
+ {
+ [MethodImpl(InliningOptions.ShortMethod)]
+ get => SimdUtils.Shuffle.MmShuffle(3, 0, 1, 2);
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void RunFallbackShuffle(ReadOnlySpan source, Span dest)
+ {
+ ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source));
+ ref uint dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest));
+ int n = source.Length / 4;
+
+ for (int i = 0; i < n; i++)
+ {
+ uint packed = Unsafe.Add(ref sBase, i);
+
+ // packed = [W Z Y X]
+ // tmp1 = [W 0 Y 0]
+ // tmp2 = [0 Z 0 X]
+ // tmp3=ROTL(16, tmp2) = [0 X 0 Z]
+ // tmp1 + tmp3 = [W X Y Z]
+ uint tmp1 = packed & 0xFF00FF00;
+ uint tmp2 = packed & 0x00FF00FF;
+ uint tmp3 = (tmp2 << 16) | (tmp2 >> 16);
+
+ Unsafe.Add(ref dBase, i) = tmp1 + tmp3;
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs b/src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs
new file mode 100644
index 0000000000..0c2b1d5082
--- /dev/null
+++ b/src/ImageSharp/Common/Helpers/Shuffle/IPad3Shuffle4.cs
@@ -0,0 +1,103 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace SixLabors.ImageSharp
+{
+ ///
+ internal interface IPad3Shuffle4 : IComponentShuffle
+ {
+ }
+
+ internal readonly struct DefaultPad3Shuffle4 : IPad3Shuffle4
+ {
+ private readonly byte p3;
+ private readonly byte p2;
+ private readonly byte p1;
+ private readonly byte p0;
+
+ public DefaultPad3Shuffle4(byte p3, byte p2, byte p1, byte p0)
+ {
+ DebugGuard.MustBeBetweenOrEqualTo(p3, 0, 3, nameof(p3));
+ DebugGuard.MustBeBetweenOrEqualTo(p2, 0, 3, nameof(p2));
+ DebugGuard.MustBeBetweenOrEqualTo(p1, 0, 3, nameof(p1));
+ DebugGuard.MustBeBetweenOrEqualTo(p0, 0, 3, nameof(p0));
+
+ this.p3 = p3;
+ this.p2 = p2;
+ this.p1 = p1;
+ this.p0 = p0;
+ this.Control = SimdUtils.Shuffle.MmShuffle(p3, p2, p1, p0);
+ }
+
+ public byte Control { get; }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void RunFallbackShuffle(ReadOnlySpan source, Span dest)
+ {
+ ref byte sBase = ref MemoryMarshal.GetReference(source);
+ ref byte dBase = ref MemoryMarshal.GetReference(dest);
+
+ int p3 = this.p3;
+ int p2 = this.p2;
+ int p1 = this.p1;
+ int p0 = this.p0;
+
+ Span temp = stackalloc byte[4];
+ ref byte t = ref MemoryMarshal.GetReference(temp);
+ ref uint tu = ref Unsafe.As(ref t);
+
+ for (int i = 0, j = 0; i < source.Length; i += 3, j += 4)
+ {
+ ref var s = ref Unsafe.Add(ref sBase, i);
+ tu = Unsafe.As(ref s) | 0xFF000000;
+
+ Unsafe.Add(ref dBase, j) = Unsafe.Add(ref t, p0);
+ Unsafe.Add(ref dBase, j + 1) = Unsafe.Add(ref t, p1);
+ Unsafe.Add(ref dBase, j + 2) = Unsafe.Add(ref t, p2);
+ Unsafe.Add(ref dBase, j + 3) = Unsafe.Add(ref t, p3);
+ }
+ }
+ }
+
+ internal readonly struct XYZWPad3Shuffle4 : IPad3Shuffle4
+ {
+ public byte Control
+ {
+ [MethodImpl(InliningOptions.ShortMethod)]
+ get => SimdUtils.Shuffle.MmShuffle(3, 2, 1, 0);
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void RunFallbackShuffle(ReadOnlySpan source, Span dest)
+ {
+ ref byte sBase = ref MemoryMarshal.GetReference(source);
+ ref byte dBase = ref MemoryMarshal.GetReference(dest);
+
+ ref byte sEnd = ref Unsafe.Add(ref sBase, source.Length);
+ ref byte sLoopEnd = ref Unsafe.Subtract(ref sEnd, 4);
+
+ while (Unsafe.IsAddressLessThan(ref sBase, ref sLoopEnd))
+ {
+ Unsafe.As(ref dBase) = Unsafe.As(ref sBase) | 0xFF000000;
+
+ sBase = ref Unsafe.Add(ref sBase, 3);
+ dBase = ref Unsafe.Add(ref dBase, 4);
+ }
+
+ while (Unsafe.IsAddressLessThan(ref sBase, ref sEnd))
+ {
+ Unsafe.Add(ref dBase, 0) = Unsafe.Add(ref sBase, 0);
+ Unsafe.Add(ref dBase, 1) = Unsafe.Add(ref sBase, 1);
+ Unsafe.Add(ref dBase, 2) = Unsafe.Add(ref sBase, 2);
+ Unsafe.Add(ref dBase, 3) = byte.MaxValue;
+
+ sBase = ref Unsafe.Add(ref sBase, 3);
+ dBase = ref Unsafe.Add(ref dBase, 4);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs
new file mode 100644
index 0000000000..61e99890e7
--- /dev/null
+++ b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle3.cs
@@ -0,0 +1,53 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace SixLabors.ImageSharp
+{
+ ///
+ internal interface IShuffle3 : IComponentShuffle
+ {
+ }
+
+ internal readonly struct DefaultShuffle3 : IShuffle3
+ {
+ private readonly byte p2;
+ private readonly byte p1;
+ private readonly byte p0;
+
+ public DefaultShuffle3(byte p2, byte p1, byte p0)
+ {
+ DebugGuard.MustBeBetweenOrEqualTo(p2, 0, 2, nameof(p2));
+ DebugGuard.MustBeBetweenOrEqualTo(p1, 0, 2, nameof(p1));
+ DebugGuard.MustBeBetweenOrEqualTo(p0, 0, 2, nameof(p0));
+
+ this.p2 = p2;
+ this.p1 = p1;
+ this.p0 = p0;
+ this.Control = SimdUtils.Shuffle.MmShuffle(3, p2, p1, p0);
+ }
+
+ public byte Control { get; }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void RunFallbackShuffle(ReadOnlySpan source, Span dest)
+ {
+ ref byte sBase = ref MemoryMarshal.GetReference(source);
+ ref byte dBase = ref MemoryMarshal.GetReference(dest);
+
+ int p2 = this.p2;
+ int p1 = this.p1;
+ int p0 = this.p0;
+
+ for (int i = 0; i < source.Length; i += 3)
+ {
+ Unsafe.Add(ref dBase, i) = Unsafe.Add(ref sBase, p0 + i);
+ Unsafe.Add(ref dBase, i + 1) = Unsafe.Add(ref sBase, p1 + i);
+ Unsafe.Add(ref dBase, i + 2) = Unsafe.Add(ref sBase, p2 + i);
+ }
+ }
+ }
+}
diff --git a/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs
new file mode 100644
index 0000000000..3ecad3c5d9
--- /dev/null
+++ b/src/ImageSharp/Common/Helpers/Shuffle/IShuffle4Slice3.cs
@@ -0,0 +1,101 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace SixLabors.ImageSharp
+{
+ ///
+ internal interface IShuffle4Slice3 : IComponentShuffle
+ {
+ }
+
+ internal readonly struct DefaultShuffle4Slice3 : IShuffle4Slice3
+ {
+ private readonly byte p2;
+ private readonly byte p1;
+ private readonly byte p0;
+
+ public DefaultShuffle4Slice3(byte p3, byte p2, byte p1, byte p0)
+ {
+ DebugGuard.MustBeBetweenOrEqualTo(p3, 0, 3, nameof(p3));
+ DebugGuard.MustBeBetweenOrEqualTo(p2, 0, 3, nameof(p2));
+ DebugGuard.MustBeBetweenOrEqualTo(p1, 0, 3, nameof(p1));
+ DebugGuard.MustBeBetweenOrEqualTo(p0, 0, 3, nameof(p0));
+
+ this.p2 = p2;
+ this.p1 = p1;
+ this.p0 = p0;
+ this.Control = SimdUtils.Shuffle.MmShuffle(p3, p2, p1, p0);
+ }
+
+ public byte Control { get; }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void RunFallbackShuffle(ReadOnlySpan source, Span dest)
+ {
+ ref byte sBase = ref MemoryMarshal.GetReference(source);
+ ref byte dBase = ref MemoryMarshal.GetReference(dest);
+
+ int p2 = this.p2;
+ int p1 = this.p1;
+ int p0 = this.p0;
+
+ for (int i = 0, j = 0; i < dest.Length; i += 3, j += 4)
+ {
+ Unsafe.Add(ref dBase, i) = Unsafe.Add(ref sBase, p0 + j);
+ Unsafe.Add(ref dBase, i + 1) = Unsafe.Add(ref sBase, p1 + j);
+ Unsafe.Add(ref dBase, i + 2) = Unsafe.Add(ref sBase, p2 + j);
+ }
+ }
+ }
+
+ internal readonly struct XYZWShuffle4Slice3 : IShuffle4Slice3
+ {
+ public byte Control
+ {
+ [MethodImpl(InliningOptions.ShortMethod)]
+ get => SimdUtils.Shuffle.MmShuffle(3, 2, 1, 0);
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public void RunFallbackShuffle(ReadOnlySpan source, Span dest)
+ {
+ ref uint sBase = ref Unsafe.As(ref MemoryMarshal.GetReference(source));
+ ref Byte3 dBase = ref Unsafe.As(ref MemoryMarshal.GetReference(dest));
+
+ int n = source.Length / 4;
+ int m = Numerics.Modulo4(n);
+ int u = n - m;
+
+ ref uint sLoopEnd = ref Unsafe.Add(ref sBase, u);
+ ref uint sEnd = ref Unsafe.Add(ref sBase, n);
+
+ while (Unsafe.IsAddressLessThan(ref sBase, ref sLoopEnd))
+ {
+ Unsafe.Add(ref dBase, 0) = Unsafe.As(ref Unsafe.Add(ref sBase, 0));
+ Unsafe.Add(ref dBase, 1) = Unsafe.As(ref Unsafe.Add(ref sBase, 1));
+ Unsafe.Add(ref dBase, 2) = Unsafe.As(ref Unsafe.Add(ref sBase, 2));
+ Unsafe.Add(ref dBase, 3) = Unsafe.As(ref Unsafe.Add(ref sBase, 3));
+
+ sBase = ref Unsafe.Add(ref sBase, 4);
+ dBase = ref Unsafe.Add(ref dBase, 4);
+ }
+
+ while (Unsafe.IsAddressLessThan(ref sBase, ref sEnd))
+ {
+ Unsafe.Add(ref dBase, 0) = Unsafe.As(ref Unsafe.Add(ref sBase, 0));
+
+ sBase = ref Unsafe.Add(ref sBase, 1);
+ dBase = ref Unsafe.Add(ref dBase, 1);
+ }
+ }
+ }
+
+ [StructLayout(LayoutKind.Explicit, Size = 3)]
+ internal readonly struct Byte3
+ {
+ }
+}
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.Avx2Intrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.Avx2Intrinsics.cs
deleted file mode 100644
index b56c92dab7..0000000000
--- a/src/ImageSharp/Common/Helpers/SimdUtils.Avx2Intrinsics.cs
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright (c) Six Labors.
-// Licensed under the Apache License, Version 2.0.
-
-#if SUPPORTS_RUNTIME_INTRINSICS
-
-using System;
-using System.Numerics;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Runtime.Intrinsics;
-using System.Runtime.Intrinsics.X86;
-
-namespace SixLabors.ImageSharp
-{
- internal static partial class SimdUtils
- {
- public static class Avx2Intrinsics
- {
- private static ReadOnlySpan PermuteMaskDeinterleave8x32 => new byte[] { 0, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0 };
-
- ///
- /// as many elements as possible, slicing them down (keeping the remainder).
- ///
- [MethodImpl(InliningOptions.ShortMethod)]
- internal static void NormalizedFloatToByteSaturateReduce(
- ref ReadOnlySpan source,
- ref Span dest)
- {
- DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!");
-
- if (Avx2.IsSupported)
- {
- int remainder = ImageMaths.ModuloP2(source.Length, Vector.Count);
- int adjustedCount = source.Length - remainder;
-
- if (adjustedCount > 0)
- {
- NormalizedFloatToByteSaturate(
- source.Slice(0, adjustedCount),
- dest.Slice(0, adjustedCount));
-
- source = source.Slice(adjustedCount);
- dest = dest.Slice(adjustedCount);
- }
- }
- }
-
- ///
- /// Implementation of , which is faster on new .NET runtime.
- ///
- ///
- /// Implementation is based on MagicScaler code:
- /// https://github.com/saucecontrol/PhotoSauce/blob/a9bd6e5162d2160419f0cf743fd4f536c079170b/src/MagicScaler/Magic/Processors/ConvertersFloat.cs#L453-L477
- ///
- internal static void NormalizedFloatToByteSaturate(
- ReadOnlySpan source,
- Span dest)
- {
- VerifySpanInput(source, dest, Vector256.Count);
-
- int n = dest.Length / Vector256.Count;
-
- ref Vector256 sourceBase =
- ref Unsafe.As>(ref MemoryMarshal.GetReference(source));
- ref Vector256 destBase = ref Unsafe.As>(ref MemoryMarshal.GetReference(dest));
-
- var maxBytes = Vector256.Create(255f);
- ref byte maskBase = ref MemoryMarshal.GetReference(PermuteMaskDeinterleave8x32);
- Vector256 mask = Unsafe.As>(ref maskBase);
-
- for (int i = 0; i < n; i++)
- {
- ref Vector256 s = ref Unsafe.Add(ref sourceBase, i * 4);
-
- Vector256 f0 = s;
- Vector256 f1 = Unsafe.Add(ref s, 1);
- Vector256 f2 = Unsafe.Add(ref s, 2);
- Vector256 f3 = Unsafe.Add(ref s, 3);
-
- Vector256 w0 = ConvertToInt32(f0, maxBytes);
- Vector256 w1 = ConvertToInt32(f1, maxBytes);
- Vector256 w2 = ConvertToInt32(f2, maxBytes);
- Vector256 w3 = ConvertToInt32(f3, maxBytes);
-
- Vector256 u0 = Avx2.PackSignedSaturate(w0, w1);
- Vector256 u1 = Avx2.PackSignedSaturate(w2, w3);
- Vector256 b = Avx2.PackUnsignedSaturate(u0, u1);
- b = Avx2.PermuteVar8x32(b.AsInt32(), mask).AsByte();
-
- Unsafe.Add(ref destBase, i) = b;
- }
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static Vector256 ConvertToInt32(Vector256 vf, Vector256 scale)
- {
- vf = Avx.Multiply(vf, scale);
- return Avx.ConvertToVector256Int32(vf);
- }
- }
- }
-}
-#endif
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs b/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs
index de6990db5b..75555f88a5 100644
--- a/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs
+++ b/src/ImageSharp/Common/Helpers/SimdUtils.BasicIntrinsics256.cs
@@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp
return;
}
- int remainder = ImageMaths.Modulo8(source.Length);
+ int remainder = Numerics.Modulo8(source.Length);
int adjustedCount = source.Length - remainder;
if (adjustedCount > 0)
@@ -64,7 +64,7 @@ namespace SixLabors.ImageSharp
return;
}
- int remainder = ImageMaths.Modulo8(source.Length);
+ int remainder = Numerics.Modulo8(source.Length);
int adjustedCount = source.Length - remainder;
if (adjustedCount > 0)
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs
index bd35d1583e..0abc0e26da 100644
--- a/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs
+++ b/src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs
@@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp
return;
}
- int remainder = ImageMaths.ModuloP2(source.Length, Vector.Count);
+ int remainder = Numerics.ModuloP2(source.Length, Vector.Count);
int adjustedCount = source.Length - remainder;
if (adjustedCount > 0)
@@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp
return;
}
- int remainder = ImageMaths.ModuloP2(source.Length, Vector.Count);
+ int remainder = Numerics.ModuloP2(source.Length, Vector.Count);
int adjustedCount = source.Length - remainder;
if (adjustedCount > 0)
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs b/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs
index 1e89aaeb83..15133770f6 100644
--- a/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs
+++ b/src/ImageSharp/Common/Helpers/SimdUtils.FallbackIntrinsics128.cs
@@ -28,7 +28,7 @@ namespace SixLabors.ImageSharp
{
DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!");
- int remainder = ImageMaths.Modulo4(source.Length);
+ int remainder = Numerics.Modulo4(source.Length);
int adjustedCount = source.Length - remainder;
if (adjustedCount > 0)
@@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp
{
DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!");
- int remainder = ImageMaths.Modulo4(source.Length);
+ int remainder = Numerics.Modulo4(source.Length);
int adjustedCount = source.Length - remainder;
if (adjustedCount > 0)
@@ -125,7 +125,7 @@ namespace SixLabors.ImageSharp
Vector4 s = Unsafe.Add(ref sBase, i);
s *= maxBytes;
s += half;
- s = Vector4Utilities.FastClamp(s, Vector4.Zero, maxBytes);
+ s = Numerics.Clamp(s, Vector4.Zero, maxBytes);
ref ByteVector4 d = ref Unsafe.Add(ref dBase, i);
d.X = (byte)s.X;
diff --git a/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs
new file mode 100644
index 0000000000..4faf577fd9
--- /dev/null
+++ b/src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs
@@ -0,0 +1,944 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+#if SUPPORTS_RUNTIME_INTRINSICS
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp
+{
+ internal static partial class SimdUtils
+ {
+ public static class HwIntrinsics
+ {
+ public static ReadOnlySpan PermuteMaskDeinterleave8x32 => new byte[] { 0, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0 };
+
+ public static ReadOnlySpan PermuteMaskEvenOdd8x32 => new byte[] { 0, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 6, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 5, 0, 0, 0, 7, 0, 0, 0 };
+
+ public static ReadOnlySpan PermuteMaskSwitchInnerDWords8x32 => new byte[] { 0, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0 };
+
+ private static ReadOnlySpan ShuffleMaskPad4Nx16 => new byte[] { 0, 1, 2, 0x80, 3, 4, 5, 0x80, 6, 7, 8, 0x80, 9, 10, 11, 0x80 };
+
+ private static ReadOnlySpan ShuffleMaskSlice4Nx16 => new byte[] { 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 0x80, 0x80, 0x80, 0x80 };
+
+ private static ReadOnlySpan ShuffleMaskShiftAlpha =>
+ new byte[]
+ {
+ 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 3, 7, 11, 15,
+ 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 3, 7, 11, 15
+ };
+
+ public static ReadOnlySpan PermuteMaskShiftAlpha8x32 =>
+ new byte[]
+ {
+ 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 4, 0, 0, 0,
+ 5, 0, 0, 0, 6, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0
+ };
+
+ ///
+ /// Shuffle single-precision (32-bit) floating-point elements in
+ /// using the control and store the results in .
+ ///
+ /// The source span of floats.
+ /// The destination span of floats.
+ /// The byte control.
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static void Shuffle4Reduce(
+ ref ReadOnlySpan source,
+ ref Span dest,
+ byte control)
+ {
+ if (Avx.IsSupported || Sse.IsSupported)
+ {
+ int remainder = Avx.IsSupported
+ ? Numerics.ModuloP2(source.Length, Vector256.Count)
+ : Numerics.ModuloP2(source.Length, Vector128.Count);
+
+ int adjustedCount = source.Length - remainder;
+
+ if (adjustedCount > 0)
+ {
+ Shuffle4(
+ source.Slice(0, adjustedCount),
+ dest.Slice(0, adjustedCount),
+ control);
+
+ source = source.Slice(adjustedCount);
+ dest = dest.Slice(adjustedCount);
+ }
+ }
+ }
+
+ ///
+ /// Shuffle 8-bit integers within 128-bit lanes in
+ /// using the control and store the results in .
+ ///
+ /// The source span of bytes.
+ /// The destination span of bytes.
+ /// The byte control.
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static void Shuffle4Reduce(
+ ref ReadOnlySpan source,
+ ref Span dest,
+ byte control)
+ {
+ if (Avx2.IsSupported || Ssse3.IsSupported)
+ {
+ int remainder = Avx2.IsSupported
+ ? Numerics.ModuloP2(source.Length, Vector256.Count)
+ : Numerics.ModuloP2(source.Length, Vector128.Count);
+
+ int adjustedCount = source.Length - remainder;
+
+ if (adjustedCount > 0)
+ {
+ Shuffle4(
+ source.Slice(0, adjustedCount),
+ dest.Slice(0, adjustedCount),
+ control);
+
+ source = source.Slice(adjustedCount);
+ dest = dest.Slice(adjustedCount);
+ }
+ }
+ }
+
+ ///
+ /// Shuffles 8-bit integer triplets within 128-bit lanes in
+ /// using the control and store the results in .
+ ///
+ /// The source span of bytes.
+ /// The destination span of bytes.
+ /// The byte control.
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static void Shuffle3Reduce(
+ ref ReadOnlySpan source,
+ ref Span dest,
+ byte control)
+ {
+ if (Ssse3.IsSupported)
+ {
+ int remainder = source.Length % (Vector128.Count * 3);
+
+ int adjustedCount = source.Length - remainder;
+
+ if (adjustedCount > 0)
+ {
+ Shuffle3(
+ source.Slice(0, adjustedCount),
+ dest.Slice(0, adjustedCount),
+ control);
+
+ source = source.Slice(adjustedCount);
+ dest = dest.Slice(adjustedCount);
+ }
+ }
+ }
+
+ ///
+ /// Pads then shuffles 8-bit integers within 128-bit lanes in
+ /// using the control and store the results in .
+ ///
+ /// The source span of bytes.
+ /// The destination span of bytes.
+ /// The byte control.
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static void Pad3Shuffle4Reduce(
+ ref ReadOnlySpan source,
+ ref Span dest,
+ byte control)
+ {
+ if (Ssse3.IsSupported)
+ {
+ int remainder = source.Length % (Vector128.Count * 3);
+
+ int sourceCount = source.Length - remainder;
+ int destCount = sourceCount * 4 / 3;
+
+ if (sourceCount > 0)
+ {
+ Pad3Shuffle4(
+ source.Slice(0, sourceCount),
+ dest.Slice(0, destCount),
+ control);
+
+ source = source.Slice(sourceCount);
+ dest = dest.Slice(destCount);
+ }
+ }
+ }
+
+ ///
+ /// Shuffles then slices 8-bit integers within 128-bit lanes in
+ /// using the control and store the results in .
+ ///
+ /// The source span of bytes.
+ /// The destination span of bytes.
+ /// The byte control.
+ [MethodImpl(InliningOptions.ShortMethod)]
+ public static void Shuffle4Slice3Reduce(
+ ref ReadOnlySpan source,
+ ref Span dest,
+ byte control)
+ {
+ if (Ssse3.IsSupported)
+ {
+ int remainder = source.Length % (Vector128.Count * 4);
+
+ int sourceCount = source.Length - remainder;
+ int destCount = sourceCount * 3 / 4;
+
+ if (sourceCount > 0)
+ {
+ Shuffle4Slice3(
+ source.Slice(0, sourceCount),
+ dest.Slice(0, destCount),
+ control);
+
+ source = source.Slice(sourceCount);
+ dest = dest.Slice(destCount);
+ }
+ }
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void Shuffle4(
+ ReadOnlySpan source,
+ Span dest,
+ byte control)
+ {
+ if (Avx.IsSupported)
+ {
+ ref Vector256 sourceBase =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(source));
+
+ ref Vector256 destBase =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(dest));
+
+ int n = dest.Length / Vector256.Count;
+ int m = Numerics.Modulo4(n);
+ int u = n - m;
+
+ for (int i = 0; i < u; i += 4)
+ {
+ ref Vector256 vd0 = ref Unsafe.Add(ref destBase, i);
+ ref Vector256 vs0 = ref Unsafe.Add(ref sourceBase, i);
+
+ vd0 = Avx.Permute(vs0, control);
+ Unsafe.Add(ref vd0, 1) = Avx.Permute(Unsafe.Add(ref vs0, 1), control);
+ Unsafe.Add(ref vd0, 2) = Avx.Permute(Unsafe.Add(ref vs0, 2), control);
+ Unsafe.Add(ref vd0, 3) = Avx.Permute(Unsafe.Add(ref vs0, 3), control);
+ }
+
+ if (m > 0)
+ {
+ for (int i = u; i < n; i++)
+ {
+ Unsafe.Add(ref destBase, i) = Avx.Permute(Unsafe.Add(ref sourceBase, i), control);
+ }
+ }
+ }
+ else
+ {
+ // Sse
+ ref Vector128 sourceBase =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(source));
+
+ ref Vector128 destBase =
+ ref Unsafe.As>(ref MemoryMarshal.GetReference(dest));
+
+ int n = dest.Length / Vector128.Count;
+ int m = Numerics.Modulo4(n);
+ int u = n - m;
+
+ for (int i = 0; i < u; i += 4)
+ {
+ ref Vector128 vd0 = ref Unsafe.Add(ref destBase, i);
+ ref Vector128 vs0 = ref Unsafe.Add(ref sourceBase, i);
+
+ vd0 = Sse.Shuffle(vs0, vs0, control);
+
+ Vector128 vs1 = Unsafe.Add(ref vs0, 1);
+ Unsafe.Add(ref vd0, 1) = Sse.Shuffle(vs1, vs1, control);
+
+ Vector128 vs2 = Unsafe.Add(ref vs0, 2);
+ Unsafe.Add(ref vd0, 2) = Sse.Shuffle(vs2, vs2, control);
+
+ Vector128 vs3 = Unsafe.Add(ref vs0, 3);
+ Unsafe.Add(ref vd0, 3) = Sse.Shuffle(vs3, vs3, control);
+ }
+
+ if (m > 0)
+ {
+ for (int i = u; i < n; i++)
+ {
+ Vector128 vs = Unsafe.Add(ref sourceBase, i);
+ Unsafe.Add(ref destBase, i) = Sse.Shuffle(vs, vs, control);
+ }
+ }
+ }
+ }
+
+ [MethodImpl(InliningOptions.ShortMethod)]
+ private static void Shuffle4(
+ ReadOnlySpan source,
+ Span dest,
+ byte control)
+ {
+ if (Avx2.IsSupported)
+ {
+ // I've chosen to do this for convenience while we determine what
+ // shuffle controls to add to the library.
+ // We can add static ROS instances if need be in the future.
+ Span bytes = stackalloc byte[Vector256