mirror of https://github.com/SixLabors/ImageSharp
1259 changed files with 19371 additions and 6750 deletions
@ -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. |
|||
########################################## |
|||
|
|||
@ -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 |
|||
|
|||
@ -0,0 +1,7 @@ |
|||
<?xml version="1.0" encoding="utf-8" ?> |
|||
<RunSettings> |
|||
<RunConfiguration> |
|||
<!--Used in conjunction with ActiveIssueAttribute to skip tests with known issues--> |
|||
<TestCaseFilter>category!=failing</TestCaseFilter> |
|||
</RunConfiguration> |
|||
</RunSettings> |
|||
@ -1 +1 @@ |
|||
Subproject commit b0d4cd98647996265a668e852574d901b27f22d6 |
|||
Subproject commit 06a733983486638b9e38197c7c6eb197ecac43e6 |
|||
@ -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 |
|||
{ |
|||
/// <summary>
|
|||
/// Extension methods for classes that implement <see cref="IComparable{T}"/>.
|
|||
/// </summary>
|
|||
internal static class ComparableExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Restricts a <see cref="byte"/> to be within a specified range.
|
|||
/// </summary>
|
|||
/// <param name="value">The value to clamp.</param>
|
|||
/// <param name="min">The minimum value. If value is less than min, min will be returned.</param>
|
|||
/// <param name="max">The maximum value. If value is greater than max, max will be returned.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="byte"/> representing the clamped value.
|
|||
/// </returns>
|
|||
[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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Restricts a <see cref="uint"/> to be within a specified range.
|
|||
/// </summary>
|
|||
/// <param name="value">The The value to clamp.</param>
|
|||
/// <param name="min">The minimum value. If value is less than min, min will be returned.</param>
|
|||
/// <param name="max">The maximum value. If value is greater than max, max will be returned.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="int"/> representing the clamped value.
|
|||
/// </returns>
|
|||
[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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Restricts a <see cref="int"/> to be within a specified range.
|
|||
/// </summary>
|
|||
/// <param name="value">The The value to clamp.</param>
|
|||
/// <param name="min">The minimum value. If value is less than min, min will be returned.</param>
|
|||
/// <param name="max">The maximum value. If value is greater than max, max will be returned.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="int"/> representing the clamped value.
|
|||
/// </returns>
|
|||
[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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Restricts a <see cref="float"/> to be within a specified range.
|
|||
/// </summary>
|
|||
/// <param name="value">The The value to clamp.</param>
|
|||
/// <param name="min">The minimum value. If value is less than min, min will be returned.</param>
|
|||
/// <param name="max">The maximum value. If value is greater than max, max will be returned.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="float"/> representing the clamped value.
|
|||
/// </returns>
|
|||
[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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Restricts a <see cref="double"/> to be within a specified range.
|
|||
/// </summary>
|
|||
/// <param name="value">The The value to clamp.</param>
|
|||
/// <param name="min">The minimum value. If value is less than min, min will be returned.</param>
|
|||
/// <param name="max">The maximum value. If value is greater than max, max will be returned.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="double"/> representing the clamped value.
|
|||
/// </returns>
|
|||
[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; |
|||
} |
|||
} |
|||
} |
|||
@ -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 |
|||
{ |
|||
/// <summary>
|
|||
/// Extension methods for <see cref="Buffer2D{T}"/>.
|
|||
/// TODO: One day rewrite all this to use SIMD intrinsics. There's a lot of scope for improvement.
|
|||
/// </summary>
|
|||
internal static class Buffer2DUtils |
|||
{ |
|||
/// <summary>
|
|||
/// Computes the sum of vectors in <paramref name="targetRow"/> weighted by the kernel weight values.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="kernel">The 1D convolution kernel.</param>
|
|||
/// <param name="sourcePixels">The source frame.</param>
|
|||
/// <param name="targetRow">The target row.</param>
|
|||
/// <param name="row">The current row.</param>
|
|||
/// <param name="column">The current column.</param>
|
|||
/// <param name="minRow">The minimum working area row.</param>
|
|||
/// <param name="maxRow">The maximum working area row.</param>
|
|||
/// <param name="minColumn">The minimum working area column.</param>
|
|||
/// <param name="maxColumn">The maximum working area column.</param>
|
|||
public static void Convolve4<TPixel>( |
|||
Span<Complex64> kernel, |
|||
Buffer2D<TPixel> sourcePixels, |
|||
Span<ComplexVector4> targetRow, |
|||
int row, |
|||
int column, |
|||
int minRow, |
|||
int maxRow, |
|||
int minColumn, |
|||
int maxColumn) |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
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<TPixel> sourceRowSpan = sourcePixels.GetRowSpan(offsetY); |
|||
var currentColor = sourceRowSpan[offsetX].ToVector4(); |
|||
|
|||
vector.Sum(Unsafe.Add(ref baseRef, i) * currentColor); |
|||
} |
|||
|
|||
targetRow[column] = vector; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Computes the sum of vectors in <paramref name="targetRow"/> weighted by the kernel weight values and accumulates the partial results.
|
|||
/// </summary>
|
|||
/// <param name="kernel">The 1D convolution kernel.</param>
|
|||
/// <param name="sourceValues">The source frame.</param>
|
|||
/// <param name="targetRow">The target row.</param>
|
|||
/// <param name="row">The current row.</param>
|
|||
/// <param name="column">The current column.</param>
|
|||
/// <param name="minRow">The minimum working area row.</param>
|
|||
/// <param name="maxRow">The maximum working area row.</param>
|
|||
/// <param name="minColumn">The minimum working area column.</param>
|
|||
/// <param name="maxColumn">The maximum working area column.</param>
|
|||
/// <param name="z">The weight factor for the real component of the complex pixel values.</param>
|
|||
/// <param name="w">The weight factor for the imaginary component of the complex pixel values.</param>
|
|||
public static void Convolve4AndAccumulatePartials( |
|||
Span<Complex64> kernel, |
|||
Buffer2D<ComplexVector4> sourceValues, |
|||
Span<Vector4> 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); |
|||
} |
|||
} |
|||
} |
|||
@ -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 |
|||
{ |
|||
/// <summary>
|
|||
/// Provides optimized static methods for common mathematical functions specific
|
|||
/// to color processing.
|
|||
/// </summary>
|
|||
internal static class ColorNumerics |
|||
{ |
|||
/// <summary>
|
|||
/// Vector for converting pixel to gray value as specified by
|
|||
/// ITU-R Recommendation BT.709.
|
|||
/// </summary>
|
|||
private static readonly Vector4 Bt709 = new Vector4(.2126f, .7152f, .0722f, 0.0f); |
|||
|
|||
/// <summary>
|
|||
/// Convert a pixel value to grayscale using ITU-R Recommendation BT.709.
|
|||
/// </summary>
|
|||
/// <param name="vector">The vector to get the luminance from.</param>
|
|||
/// <param name="luminanceLevels">
|
|||
/// The number of luminance levels (256 for 8 bit, 65536 for 16 bit grayscale images).
|
|||
/// </param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int GetBT709Luminance(ref Vector4 vector, int luminanceLevels) |
|||
=> (int)MathF.Round(Vector4.Dot(vector, Bt709) * (luminanceLevels - 1)); |
|||
|
|||
/// <summary>
|
|||
/// Gets the luminance from the rgb components using the formula
|
|||
/// as specified by ITU-R Recommendation BT.709.
|
|||
/// </summary>
|
|||
/// <param name="r">The red component.</param>
|
|||
/// <param name="g">The green component.</param>
|
|||
/// <param name="b">The blue component.</param>
|
|||
/// <returns>The <see cref="byte"/>.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static byte Get8BitBT709Luminance(byte r, byte g, byte b) |
|||
=> (byte)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F); |
|||
|
|||
/// <summary>
|
|||
/// Gets the luminance from the rgb components using the formula as
|
|||
/// specified by ITU-R Recommendation BT.709.
|
|||
/// </summary>
|
|||
/// <param name="r">The red component.</param>
|
|||
/// <param name="g">The green component.</param>
|
|||
/// <param name="b">The blue component.</param>
|
|||
/// <returns>The <see cref="ushort"/>.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static ushort Get16BitBT709Luminance(ushort r, ushort g, ushort b) |
|||
=> (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F); |
|||
|
|||
/// <summary>
|
|||
/// Gets the luminance from the rgb components using the formula as specified
|
|||
/// by ITU-R Recommendation BT.709.
|
|||
/// </summary>
|
|||
/// <param name="r">The red component.</param>
|
|||
/// <param name="g">The green component.</param>
|
|||
/// <param name="b">The blue component.</param>
|
|||
/// <returns>The <see cref="ushort"/>.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static ushort Get16BitBT709Luminance(float r, float g, float b) |
|||
=> (ushort)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F); |
|||
|
|||
/// <summary>
|
|||
/// Scales a value from a 16 bit <see cref="ushort"/> to an
|
|||
/// 8 bit <see cref="byte"/> equivalent.
|
|||
/// </summary>
|
|||
/// <param name="component">The 8 bit component value.</param>
|
|||
/// <returns>The <see cref="byte"/></returns>
|
|||
[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); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Scales a value from an 8 bit <see cref="byte"/> to
|
|||
/// an 16 bit <see cref="ushort"/> equivalent.
|
|||
/// </summary>
|
|||
/// <param name="component">The 8 bit component value.</param>
|
|||
/// <returns>The <see cref="ushort"/></returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static ushort UpscaleFrom8BitTo16Bit(byte component) |
|||
=> (ushort)(component * 257); |
|||
|
|||
/// <summary>
|
|||
/// Returns how many bits are required to store the specified number of colors.
|
|||
/// Performs a Log2() on the value.
|
|||
/// </summary>
|
|||
/// <param name="colors">The number of colors.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="int"/>
|
|||
/// </returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int GetBitsNeededForColorDepth(int colors) |
|||
=> Math.Max(1, (int)Math.Ceiling(Math.Log(colors, 2))); |
|||
|
|||
/// <summary>
|
|||
/// Returns how many colors will be created by the specified number of bits.
|
|||
/// </summary>
|
|||
/// <param name="bitDepth">The bit depth.</param>
|
|||
/// <returns>The <see cref="int"/></returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int GetColorCountForBitDepth(int bitDepth) |
|||
=> 1 << bitDepth; |
|||
|
|||
/// <summary>
|
|||
/// Transforms a vector by the given color matrix.
|
|||
/// </summary>
|
|||
/// <param name="vector">The source vector.</param>
|
|||
/// <param name="matrix">The transformation color matrix.</param>
|
|||
[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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Bulk variant of <see cref="Transform(ref Vector4, ref ColorMatrix)"/>.
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors</param>
|
|||
/// <param name="matrix">The transformation color matrix.</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Transform(Span<Vector4> 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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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 |
|||
{ |
|||
/// <summary>
|
|||
/// Extension methods for <see cref="DenseMatrix{T}"/>.
|
|||
/// TODO: One day rewrite all this to use SIMD intrinsics. There's a lot of scope for improvement.
|
|||
/// </summary>
|
|||
internal static class DenseMatrixUtils |
|||
{ |
|||
/// <summary>
|
|||
/// Computes the sum of vectors in the span referenced by <paramref name="targetRowRef"/> weighted by the two kernel weight values.
|
|||
/// Using this method the convolution filter is not applied to alpha in addition to the color channels.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="matrixY">The vertical dense matrix.</param>
|
|||
/// <param name="matrixX">The horizontal dense matrix.</param>
|
|||
/// <param name="sourcePixels">The source frame.</param>
|
|||
/// <param name="targetRowRef">The target row base reference.</param>
|
|||
/// <param name="row">The current row.</param>
|
|||
/// <param name="column">The current column.</param>
|
|||
/// <param name="minRow">The minimum working area row.</param>
|
|||
/// <param name="maxRow">The maximum working area row.</param>
|
|||
/// <param name="minColumn">The minimum working area column.</param>
|
|||
/// <param name="maxColumn">The maximum working area column.</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void Convolve2D3<TPixel>( |
|||
in DenseMatrix<float> matrixY, |
|||
in DenseMatrix<float> matrixX, |
|||
Buffer2D<TPixel> sourcePixels, |
|||
ref Vector4 targetRowRef, |
|||
int row, |
|||
int column, |
|||
int minRow, |
|||
int maxRow, |
|||
int minColumn, |
|||
int maxColumn) |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Computes the sum of vectors in the span referenced by <paramref name="targetRowRef"/> weighted by the two kernel weight values.
|
|||
/// Using this method the convolution filter is applied to alpha in addition to the color channels.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="matrixY">The vertical dense matrix.</param>
|
|||
/// <param name="matrixX">The horizontal dense matrix.</param>
|
|||
/// <param name="sourcePixels">The source frame.</param>
|
|||
/// <param name="targetRowRef">The target row base reference.</param>
|
|||
/// <param name="row">The current row.</param>
|
|||
/// <param name="column">The current column.</param>
|
|||
/// <param name="minRow">The minimum working area row.</param>
|
|||
/// <param name="maxRow">The maximum working area row.</param>
|
|||
/// <param name="minColumn">The minimum working area column.</param>
|
|||
/// <param name="maxColumn">The maximum working area column.</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void Convolve2D4<TPixel>( |
|||
in DenseMatrix<float> matrixY, |
|||
in DenseMatrix<float> matrixX, |
|||
Buffer2D<TPixel> sourcePixels, |
|||
ref Vector4 targetRowRef, |
|||
int row, |
|||
int column, |
|||
int minRow, |
|||
int maxRow, |
|||
int minColumn, |
|||
int maxColumn) |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
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<TPixel>( |
|||
in DenseMatrix<float> matrixY, |
|||
in DenseMatrix<float> matrixX, |
|||
Buffer2D<TPixel> sourcePixels, |
|||
int row, |
|||
int column, |
|||
int minRow, |
|||
int maxRow, |
|||
int minColumn, |
|||
int maxColumn, |
|||
out Vector4 vector) |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
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<TPixel> 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)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Computes the sum of vectors in the span referenced by <paramref name="targetRowRef"/> weighted by the kernel weight values.
|
|||
/// Using this method the convolution filter is not applied to alpha in addition to the color channels.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="matrix">The dense matrix.</param>
|
|||
/// <param name="sourcePixels">The source frame.</param>
|
|||
/// <param name="targetRowRef">The target row base reference.</param>
|
|||
/// <param name="row">The current row.</param>
|
|||
/// <param name="column">The current column.</param>
|
|||
/// <param name="minRow">The minimum working area row.</param>
|
|||
/// <param name="maxRow">The maximum working area row.</param>
|
|||
/// <param name="minColumn">The minimum working area column.</param>
|
|||
/// <param name="maxColumn">The maximum working area column.</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void Convolve3<TPixel>( |
|||
in DenseMatrix<float> matrix, |
|||
Buffer2D<TPixel> sourcePixels, |
|||
ref Vector4 targetRowRef, |
|||
int row, |
|||
int column, |
|||
int minRow, |
|||
int maxRow, |
|||
int minColumn, |
|||
int maxColumn) |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Computes the sum of vectors in the span referenced by <paramref name="targetRowRef"/> weighted by the kernel weight values.
|
|||
/// Using this method the convolution filter is applied to alpha in addition to the color channels.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="matrix">The dense matrix.</param>
|
|||
/// <param name="sourcePixels">The source frame.</param>
|
|||
/// <param name="targetRowRef">The target row base reference.</param>
|
|||
/// <param name="row">The current row.</param>
|
|||
/// <param name="column">The current column.</param>
|
|||
/// <param name="minRow">The minimum working area row.</param>
|
|||
/// <param name="maxRow">The maximum working area row.</param>
|
|||
/// <param name="minColumn">The minimum working area column.</param>
|
|||
/// <param name="maxColumn">The maximum working area column.</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void Convolve4<TPixel>( |
|||
in DenseMatrix<float> matrix, |
|||
Buffer2D<TPixel> sourcePixels, |
|||
ref Vector4 targetRowRef, |
|||
int row, |
|||
int column, |
|||
int minRow, |
|||
int maxRow, |
|||
int minColumn, |
|||
int maxColumn) |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
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<TPixel>( |
|||
in DenseMatrix<float> matrix, |
|||
Buffer2D<TPixel> sourcePixels, |
|||
int row, |
|||
int column, |
|||
int minRow, |
|||
int maxRow, |
|||
int minColumn, |
|||
int maxColumn, |
|||
ref Vector4 vector) |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
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<TPixel> 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; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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 |
|||
{ |
|||
/// <summary>
|
|||
/// Provides common mathematical methods.
|
|||
/// </summary>
|
|||
internal static class ImageMaths |
|||
{ |
|||
/// <summary>
|
|||
/// Vector for converting pixel to gray value as specified by ITU-R Recommendation BT.709.
|
|||
/// </summary>
|
|||
private static readonly Vector4 Bt709 = new Vector4(.2126f, .7152f, .0722f, 0.0f); |
|||
|
|||
/// <summary>
|
|||
/// Convert a pixel value to grayscale using ITU-R Recommendation BT.709.
|
|||
/// </summary>
|
|||
/// <param name="vector">The vector to get the luminance from.</param>
|
|||
/// <param name="luminanceLevels">The number of luminance levels (256 for 8 bit, 65536 for 16 bit grayscale images)</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static int GetBT709Luminance(ref Vector4 vector, int luminanceLevels) |
|||
=> (int)MathF.Round(Vector4.Dot(vector, Bt709) * (luminanceLevels - 1)); |
|||
|
|||
/// <summary>
|
|||
/// Gets the luminance from the rgb components using the formula as specified by ITU-R Recommendation BT.709.
|
|||
/// </summary>
|
|||
/// <param name="r">The red component.</param>
|
|||
/// <param name="g">The green component.</param>
|
|||
/// <param name="b">The blue component.</param>
|
|||
/// <returns>The <see cref="byte"/>.</returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static byte Get8BitBT709Luminance(byte r, byte g, byte b) => |
|||
(byte)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F); |
|||
|
|||
/// <summary>
|
|||
/// Gets the luminance from the rgb components using the formula as specified by ITU-R Recommendation BT.709.
|
|||
/// </summary>
|
|||
/// <param name="r">The red component.</param>
|
|||
/// <param name="g">The green component.</param>
|
|||
/// <param name="b">The blue component.</param>
|
|||
/// <returns>The <see cref="ushort"/>.</returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static ushort Get16BitBT709Luminance(ushort r, ushort g, ushort b) => |
|||
(ushort)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F); |
|||
|
|||
/// <summary>
|
|||
/// Gets the luminance from the rgb components using the formula as specified by ITU-R Recommendation BT.709.
|
|||
/// </summary>
|
|||
/// <param name="r">The red component.</param>
|
|||
/// <param name="g">The green component.</param>
|
|||
/// <param name="b">The blue component.</param>
|
|||
/// <returns>The <see cref="ushort"/>.</returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static ushort Get16BitBT709Luminance(float r, float g, float b) => |
|||
(ushort)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F); |
|||
|
|||
/// <summary>
|
|||
/// Scales a value from a 16 bit <see cref="ushort"/> to it's 8 bit <see cref="byte"/> equivalent.
|
|||
/// </summary>
|
|||
/// <param name="component">The 8 bit component value.</param>
|
|||
/// <returns>The <see cref="byte"/></returns>
|
|||
[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); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Scales a value from an 8 bit <see cref="byte"/> to it's 16 bit <see cref="ushort"/> equivalent.
|
|||
/// </summary>
|
|||
/// <param name="component">The 8 bit component value.</param>
|
|||
/// <returns>The <see cref="ushort"/></returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static ushort UpscaleFrom8BitTo16Bit(byte component) => (ushort)(component * 257); |
|||
|
|||
/// <summary>
|
|||
/// Determine the Greatest CommonDivisor (GCD) of two numbers.
|
|||
/// </summary>
|
|||
public static int GreatestCommonDivisor(int a, int b) |
|||
{ |
|||
while (b != 0) |
|||
{ |
|||
int temp = b; |
|||
b = a % b; |
|||
a = temp; |
|||
} |
|||
|
|||
return a; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determine the Least Common Multiple (LCM) of two numbers.
|
|||
/// </summary>
|
|||
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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculates <paramref name="x"/> % 4
|
|||
/// </summary>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static int Modulo4(int x) => x & 3; |
|||
|
|||
/// <summary>
|
|||
/// Calculates <paramref name="x"/> % 8
|
|||
/// </summary>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static int Modulo8(int x) => x & 7; |
|||
|
|||
/// <summary>
|
|||
/// Fast (x mod m) calculator, with the restriction that
|
|||
/// <paramref name="m"/> should be power of 2.
|
|||
/// </summary>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static int ModuloP2(int x, int m) => x & (m - 1); |
|||
|
|||
/// <summary>
|
|||
/// Returns the absolute value of a 32-bit signed integer. Uses bit shifting to speed up the operation.
|
|||
/// </summary>
|
|||
/// <param name="x">
|
|||
/// A number that is greater than <see cref="int.MinValue"/>, but less than or equal to <see cref="int.MaxValue"/>
|
|||
/// </param>
|
|||
/// <returns>The <see cref="int"/></returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static int FastAbs(int x) |
|||
{ |
|||
int y = x >> 31; |
|||
return (x ^ y) - y; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a specified number raised to the power of 2
|
|||
/// </summary>
|
|||
/// <param name="x">A single-precision floating-point number</param>
|
|||
/// <returns>The number <paramref name="x" /> raised to the power of 2.</returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static float Pow2(float x) => x * x; |
|||
|
|||
/// <summary>
|
|||
/// Returns a specified number raised to the power of 3
|
|||
/// </summary>
|
|||
/// <param name="x">A single-precision floating-point number</param>
|
|||
/// <returns>The number <paramref name="x" /> raised to the power of 3.</returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static float Pow3(float x) => x * x * x; |
|||
|
|||
/// <summary>
|
|||
/// Returns how many bits are required to store the specified number of colors.
|
|||
/// Performs a Log2() on the value.
|
|||
/// </summary>
|
|||
/// <param name="colors">The number of colors.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="int"/>
|
|||
/// </returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static int GetBitsNeededForColorDepth(int colors) => Math.Max(1, (int)Math.Ceiling(Math.Log(colors, 2))); |
|||
|
|||
/// <summary>
|
|||
/// Returns how many colors will be created by the specified number of bits.
|
|||
/// </summary>
|
|||
/// <param name="bitDepth">The bit depth.</param>
|
|||
/// <returns>The <see cref="int"/></returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static int GetColorCountForBitDepth(int bitDepth) => 1 << bitDepth; |
|||
|
|||
/// <summary>
|
|||
/// Implementation of 1D Gaussian G(x) function
|
|||
/// </summary>
|
|||
/// <param name="x">The x provided to G(x).</param>
|
|||
/// <param name="sigma">The spread of the blur.</param>
|
|||
/// <returns>The Gaussian G(x)</returns>
|
|||
[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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the result of a normalized sine cardinal function for the given value.
|
|||
/// SinC(x) = sin(pi*x)/(pi*x).
|
|||
/// </summary>
|
|||
/// <param name="f">A single-precision floating-point number to calculate the result for.</param>
|
|||
/// <returns>
|
|||
/// The sine cardinal of <paramref name="f" />.
|
|||
/// </returns>
|
|||
[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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the bounding <see cref="Rectangle"/> from the given points.
|
|||
/// </summary>
|
|||
/// <param name="topLeft">
|
|||
/// The <see cref="Point"/> designating the top left position.
|
|||
/// </param>
|
|||
/// <param name="bottomRight">
|
|||
/// The <see cref="Point"/> designating the bottom right position.
|
|||
/// </param>
|
|||
/// <returns>
|
|||
/// The bounding <see cref="Rectangle"/>.
|
|||
/// </returns>
|
|||
[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); |
|||
|
|||
/// <summary>
|
|||
/// Finds the bounding rectangle based on the first instance of any color component other
|
|||
/// than the given one.
|
|||
/// </summary>
|
|||
/// <typeparam name="TPixel">The pixel format.</typeparam>
|
|||
/// <param name="bitmap">The <see cref="Image{TPixel}"/> to search within.</param>
|
|||
/// <param name="componentValue">The color component value to remove.</param>
|
|||
/// <param name="channel">The <see cref="RgbaComponent"/> channel to test against.</param>
|
|||
/// <returns>
|
|||
/// The <see cref="Rectangle"/>.
|
|||
/// </returns>
|
|||
public static Rectangle GetFilteredBoundingRectangle<TPixel>(ImageFrame<TPixel> bitmap, float componentValue, RgbaComponent channel = RgbaComponent.B) |
|||
where TPixel : unmanaged, IPixel<TPixel> |
|||
{ |
|||
int width = bitmap.Width; |
|||
int height = bitmap.Height; |
|||
Point topLeft = default; |
|||
Point bottomRight = default; |
|||
|
|||
Func<ImageFrame<TPixel>, 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<TPixel> 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<TPixel> 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<TPixel> 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<TPixel> 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); |
|||
} |
|||
} |
|||
} |
|||
@ -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 |
|||
{ |
|||
/// <summary>
|
|||
/// Provides optimized static methods for trigonometric, logarithmic,
|
|||
/// and other common mathematical functions.
|
|||
/// </summary>
|
|||
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
|
|||
|
|||
/// <summary>
|
|||
/// Determine the Greatest CommonDivisor (GCD) of two numbers.
|
|||
/// </summary>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int GreatestCommonDivisor(int a, int b) |
|||
{ |
|||
while (b != 0) |
|||
{ |
|||
int temp = b; |
|||
b = a % b; |
|||
a = temp; |
|||
} |
|||
|
|||
return a; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Determine the Least Common Multiple (LCM) of two numbers.
|
|||
/// See https://en.wikipedia.org/wiki/Least_common_multiple#Reduction_by_the_greatest_common_divisor.
|
|||
/// </summary>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int LeastCommonMultiple(int a, int b) |
|||
=> a / GreatestCommonDivisor(a, b) * b; |
|||
|
|||
/// <summary>
|
|||
/// Calculates <paramref name="x"/> % 2
|
|||
/// </summary>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int Modulo2(int x) => x & 1; |
|||
|
|||
/// <summary>
|
|||
/// Calculates <paramref name="x"/> % 4
|
|||
/// </summary>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int Modulo4(int x) => x & 3; |
|||
|
|||
/// <summary>
|
|||
/// Calculates <paramref name="x"/> % 8
|
|||
/// </summary>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int Modulo8(int x) => x & 7; |
|||
|
|||
/// <summary>
|
|||
/// Fast (x mod m) calculator, with the restriction that
|
|||
/// <paramref name="m"/> should be power of 2.
|
|||
/// </summary>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int ModuloP2(int x, int m) => x & (m - 1); |
|||
|
|||
/// <summary>
|
|||
/// Returns the absolute value of a 32-bit signed integer.
|
|||
/// Uses bit shifting to speed up the operation compared to <see cref="Math"/>.
|
|||
/// </summary>
|
|||
/// <param name="x">
|
|||
/// A number that is greater than <see cref="int.MinValue"/>, but less than
|
|||
/// or equal to <see cref="int.MaxValue"/>
|
|||
/// </param>
|
|||
/// <returns>The <see cref="int"/></returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int Abs(int x) |
|||
{ |
|||
int y = x >> 31; |
|||
return (x ^ y) - y; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns a specified number raised to the power of 2
|
|||
/// </summary>
|
|||
/// <param name="x">A single-precision floating-point number</param>
|
|||
/// <returns>The number <paramref name="x" /> raised to the power of 2.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static float Pow2(float x) => x * x; |
|||
|
|||
/// <summary>
|
|||
/// Returns a specified number raised to the power of 3
|
|||
/// </summary>
|
|||
/// <param name="x">A single-precision floating-point number</param>
|
|||
/// <returns>The number <paramref name="x" /> raised to the power of 3.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static float Pow3(float x) => x * x * x; |
|||
|
|||
/// <summary>
|
|||
/// Implementation of 1D Gaussian G(x) function
|
|||
/// </summary>
|
|||
/// <param name="x">The x provided to G(x).</param>
|
|||
/// <param name="sigma">The spread of the blur.</param>
|
|||
/// <returns>The Gaussian G(x)</returns>
|
|||
[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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the result of a normalized sine cardinal function for the given value.
|
|||
/// SinC(x) = sin(pi*x)/(pi*x).
|
|||
/// </summary>
|
|||
/// <param name="f">A single-precision floating-point number to calculate the result for.</param>
|
|||
/// <returns>
|
|||
/// The sine cardinal of <paramref name="f" />.
|
|||
/// </returns>
|
|||
[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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the value clamped to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="value">The value to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
/// <returns>The clamped <see cref="byte"/>.</returns>
|
|||
[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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the value clamped to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="value">The value to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
/// <returns>The clamped <see cref="uint"/>.</returns>
|
|||
[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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the value clamped to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="value">The value to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
/// <returns>The clamped <see cref="uint"/>.</returns>
|
|||
[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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the value clamped to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="value">The value to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
/// <returns>The clamped <see cref="float"/>.</returns>
|
|||
[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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the value clamped to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="value">The value to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
/// <returns>The clamped <see cref="double"/>.</returns>
|
|||
[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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Returns the value clamped to the inclusive range of min and max.
|
|||
/// 5x Faster than <see cref="Vector4.Clamp(Vector4, Vector4, Vector4)"/>
|
|||
/// on platforms < NET 5.
|
|||
/// </summary>
|
|||
/// <param name="value">The value to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
/// <returns>The clamped <see cref="Vector4"/>.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector4 Clamp(Vector4 value, Vector4 min, Vector4 max) |
|||
=> Vector4.Min(Vector4.Max(value, min), max); |
|||
|
|||
/// <summary>
|
|||
/// Clamps the span values to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="span">The span containing the values to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Clamp(Span<byte> span, byte min, byte max) |
|||
{ |
|||
Span<byte> 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); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clamps the span values to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="span">The span containing the values to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Clamp(Span<uint> span, uint min, uint max) |
|||
{ |
|||
Span<uint> 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); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clamps the span values to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="span">The span containing the values to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Clamp(Span<int> span, int min, int max) |
|||
{ |
|||
Span<int> 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); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clamps the span values to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="span">The span containing the values to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Clamp(Span<float> span, float min, float max) |
|||
{ |
|||
Span<float> 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); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clamps the span values to the inclusive range of min and max.
|
|||
/// </summary>
|
|||
/// <param name="span">The span containing the values to clamp.</param>
|
|||
/// <param name="min">The minimum inclusive value.</param>
|
|||
/// <param name="max">The maximum inclusive value.</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Clamp(Span<double> span, double min, double max) |
|||
{ |
|||
Span<double> 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<T>(Span<T> span, T min, T max) |
|||
where T : unmanaged |
|||
{ |
|||
if (Vector.IsHardwareAccelerated && span.Length >= Vector<T>.Count) |
|||
{ |
|||
int remainder = ModuloP2(span.Length, Vector<T>.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<T>(Span<T> span, T min, T max) |
|||
where T : unmanaged |
|||
{ |
|||
ref T sRef = ref MemoryMarshal.GetReference(span); |
|||
var vmin = new Vector<T>(min); |
|||
var vmax = new Vector<T>(max); |
|||
|
|||
int n = span.Length / Vector<T>.Count; |
|||
int m = Modulo4(n); |
|||
int u = n - m; |
|||
|
|||
ref Vector<T> vs0 = ref Unsafe.As<T, Vector<T>>(ref MemoryMarshal.GetReference(span)); |
|||
ref Vector<T> vs1 = ref Unsafe.Add(ref vs0, 1); |
|||
ref Vector<T> vs2 = ref Unsafe.Add(ref vs0, 2); |
|||
ref Vector<T> vs3 = ref Unsafe.Add(ref vs0, 3); |
|||
ref Vector<T> 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); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Pre-multiplies the "x", "y", "z" components of a vector by its "w" component leaving the "w" component intact.
|
|||
/// </summary>
|
|||
/// <param name="source">The <see cref="Vector4"/> to premultiply</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Premultiply(ref Vector4 source) |
|||
{ |
|||
float w = source.W; |
|||
source *= w; |
|||
source.W = w; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reverses the result of premultiplying a vector via <see cref="Premultiply(ref Vector4)"/>.
|
|||
/// </summary>
|
|||
/// <param name="source">The <see cref="Vector4"/> to premultiply</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void UnPremultiply(ref Vector4 source) |
|||
{ |
|||
float w = source.W; |
|||
source /= w; |
|||
source.W = w; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Bulk variant of <see cref="Premultiply(ref Vector4)"/>
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Premultiply(Span<Vector4> vectors) |
|||
{ |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
if (Avx2.IsSupported && vectors.Length >= 2) |
|||
{ |
|||
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float>
|
|||
ref Vector256<float> vectorsBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(vectors)); |
|||
ref Vector256<float> vectorsLast = ref Unsafe.Add(ref vectorsBase, (IntPtr)((uint)vectors.Length / 2u)); |
|||
|
|||
while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast)) |
|||
{ |
|||
Vector256<float> source = vectorsBase; |
|||
Vector256<float> 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); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Bulk variant of <see cref="UnPremultiply(ref Vector4)"/>
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void UnPremultiply(Span<Vector4> vectors) |
|||
{ |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
if (Avx2.IsSupported && vectors.Length >= 2) |
|||
{ |
|||
// Divide by 2 as 4 elements per Vector4 and 8 per Vector256<float>
|
|||
ref Vector256<float> vectorsBase = ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(vectors)); |
|||
ref Vector256<float> vectorsLast = ref Unsafe.Add(ref vectorsBase, (IntPtr)((uint)vectors.Length / 2u)); |
|||
|
|||
while (Unsafe.IsAddressLessThan(ref vectorsBase, ref vectorsLast)) |
|||
{ |
|||
Vector256<float> source = vectorsBase; |
|||
Vector256<float> 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); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculates the cube pow of all the XYZ channels of the input vectors.
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static unsafe void CubePowOnXYZ(Span<Vector4> 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); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Calculates the cube root of all the XYZ channels of the input vectors.
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static unsafe void CubeRootOnXYZ(Span<Vector4> vectors) |
|||
{ |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
if (Sse41.IsSupported) |
|||
{ |
|||
ref Vector128<float> vectors128Ref = ref Unsafe.As<Vector4, Vector128<float>>(ref MemoryMarshal.GetReference(vectors)); |
|||
ref Vector128<float> vectors128End = ref Unsafe.Add(ref vectors128Ref, vectors.Length); |
|||
|
|||
var v128_341 = Vector128.Create(341); |
|||
Vector128<int> v128_negativeZero = Vector128.Create(-0.0f).AsInt32(); |
|||
Vector128<int> 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<float> vecx = vectors128Ref; |
|||
Vector128<int> 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<float> 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
|
|||
|
|||
/// <summary>
|
|||
/// Performs a linear interpolation between two values based on the given weighting.
|
|||
/// </summary>
|
|||
/// <param name="value1">The first value.</param>
|
|||
/// <param name="value2">The second value.</param>
|
|||
/// <param name="amount">Values between 0 and 1 that indicates the weight of <paramref name="value2"/>.</param>
|
|||
/// <returns>The <see cref="Vector256{Single}"/>.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Vector256<float> Lerp( |
|||
in Vector256<float> value1, |
|||
in Vector256<float> value2, |
|||
in Vector256<float> amount) |
|||
{ |
|||
Vector256<float> diff = Avx.Subtract(value2, value1); |
|||
if (Fma.IsSupported) |
|||
{ |
|||
return Fma.MultiplyAdd(diff, amount, value1); |
|||
} |
|||
else |
|||
{ |
|||
return Avx.Add(Avx.Multiply(diff, amount), value1); |
|||
} |
|||
} |
|||
#endif
|
|||
|
|||
/// <summary>
|
|||
/// Performs a linear interpolation between two values based on the given weighting.
|
|||
/// </summary>
|
|||
/// <param name="value1">The first value.</param>
|
|||
/// <param name="value2">The second value.</param>
|
|||
/// <param name="amount">A value between 0 and 1 that indicates the weight of <paramref name="value2"/>.</param>
|
|||
/// <returns>The <see cref="float"/>.</returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static float Lerp(float value1, float value2, float amount) |
|||
=> ((value2 - value1) * amount) + value1; |
|||
} |
|||
} |
|||
@ -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 |
|||
{ |
|||
/// <summary>
|
|||
/// Defines the contract for methods that allow the shuffling of pixel components.
|
|||
/// Used for shuffling on platforms that do not support Hardware Intrinsics.
|
|||
/// </summary>
|
|||
internal interface IComponentShuffle |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the shuffle control.
|
|||
/// </summary>
|
|||
byte Control { get; } |
|||
|
|||
/// <summary>
|
|||
/// Shuffle 8-bit integers within 128-bit lanes in <paramref name="source"/>
|
|||
/// using the control and store the results in <paramref name="dest"/>.
|
|||
/// </summary>
|
|||
/// <param name="source">The source span of bytes.</param>
|
|||
/// <param name="dest">The destination span of bytes.</param>
|
|||
void RunFallbackShuffle(ReadOnlySpan<byte> source, Span<byte> dest); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
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<byte>(p3, 0, 3, nameof(p3)); |
|||
DebugGuard.MustBeBetweenOrEqualTo<byte>(p2, 0, 3, nameof(p2)); |
|||
DebugGuard.MustBeBetweenOrEqualTo<byte>(p1, 0, 3, nameof(p1)); |
|||
DebugGuard.MustBeBetweenOrEqualTo<byte>(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<byte> source, Span<byte> 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<byte> source, Span<byte> dest) |
|||
{ |
|||
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source)); |
|||
ref uint dBase = ref Unsafe.As<byte, uint>(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<byte> source, Span<byte> dest) |
|||
{ |
|||
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source)); |
|||
ref uint dBase = ref Unsafe.As<byte, uint>(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<byte> source, Span<byte> dest) |
|||
{ |
|||
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source)); |
|||
ref uint dBase = ref Unsafe.As<byte, uint>(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<byte> source, Span<byte> dest) |
|||
{ |
|||
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source)); |
|||
ref uint dBase = ref Unsafe.As<byte, uint>(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; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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 |
|||
{ |
|||
/// <inheritdoc/>
|
|||
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<byte>(p3, 0, 3, nameof(p3)); |
|||
DebugGuard.MustBeBetweenOrEqualTo<byte>(p2, 0, 3, nameof(p2)); |
|||
DebugGuard.MustBeBetweenOrEqualTo<byte>(p1, 0, 3, nameof(p1)); |
|||
DebugGuard.MustBeBetweenOrEqualTo<byte>(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<byte> source, Span<byte> 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<byte> temp = stackalloc byte[4]; |
|||
ref byte t = ref MemoryMarshal.GetReference(temp); |
|||
ref uint tu = ref Unsafe.As<byte, uint>(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<byte, uint>(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<byte> source, Span<byte> 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<byte, uint>(ref dBase) = Unsafe.As<byte, uint>(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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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 |
|||
{ |
|||
/// <inheritdoc/>
|
|||
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<byte>(p2, 0, 2, nameof(p2)); |
|||
DebugGuard.MustBeBetweenOrEqualTo<byte>(p1, 0, 2, nameof(p1)); |
|||
DebugGuard.MustBeBetweenOrEqualTo<byte>(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<byte> source, Span<byte> 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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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 |
|||
{ |
|||
/// <inheritdoc/>
|
|||
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<byte>(p3, 0, 3, nameof(p3)); |
|||
DebugGuard.MustBeBetweenOrEqualTo<byte>(p2, 0, 3, nameof(p2)); |
|||
DebugGuard.MustBeBetweenOrEqualTo<byte>(p1, 0, 3, nameof(p1)); |
|||
DebugGuard.MustBeBetweenOrEqualTo<byte>(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<byte> source, Span<byte> 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<byte> source, Span<byte> dest) |
|||
{ |
|||
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source)); |
|||
ref Byte3 dBase = ref Unsafe.As<byte, Byte3>(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<uint, Byte3>(ref Unsafe.Add(ref sBase, 0)); |
|||
Unsafe.Add(ref dBase, 1) = Unsafe.As<uint, Byte3>(ref Unsafe.Add(ref sBase, 1)); |
|||
Unsafe.Add(ref dBase, 2) = Unsafe.As<uint, Byte3>(ref Unsafe.Add(ref sBase, 2)); |
|||
Unsafe.Add(ref dBase, 3) = Unsafe.As<uint, Byte3>(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<uint, Byte3>(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 |
|||
{ |
|||
} |
|||
} |
|||
@ -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<byte> 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 }; |
|||
|
|||
/// <summary>
|
|||
/// <see cref="NormalizedFloatToByteSaturate"/> as many elements as possible, slicing them down (keeping the remainder).
|
|||
/// </summary>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
internal static void NormalizedFloatToByteSaturateReduce( |
|||
ref ReadOnlySpan<float> source, |
|||
ref Span<byte> 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<byte>.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); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Implementation of <see cref="SimdUtils.NormalizedFloatToByteSaturate"/>, which is faster on new .NET runtime.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Implementation is based on MagicScaler code:
|
|||
/// https://github.com/saucecontrol/PhotoSauce/blob/a9bd6e5162d2160419f0cf743fd4f536c079170b/src/MagicScaler/Magic/Processors/ConvertersFloat.cs#L453-L477
|
|||
/// </remarks>
|
|||
internal static void NormalizedFloatToByteSaturate( |
|||
ReadOnlySpan<float> source, |
|||
Span<byte> dest) |
|||
{ |
|||
VerifySpanInput(source, dest, Vector256<byte>.Count); |
|||
|
|||
int n = dest.Length / Vector256<byte>.Count; |
|||
|
|||
ref Vector256<float> sourceBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(source)); |
|||
ref Vector256<byte> destBase = ref Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(dest)); |
|||
|
|||
var maxBytes = Vector256.Create(255f); |
|||
ref byte maskBase = ref MemoryMarshal.GetReference(PermuteMaskDeinterleave8x32); |
|||
Vector256<int> mask = Unsafe.As<byte, Vector256<int>>(ref maskBase); |
|||
|
|||
for (int i = 0; i < n; i++) |
|||
{ |
|||
ref Vector256<float> s = ref Unsafe.Add(ref sourceBase, i * 4); |
|||
|
|||
Vector256<float> f0 = s; |
|||
Vector256<float> f1 = Unsafe.Add(ref s, 1); |
|||
Vector256<float> f2 = Unsafe.Add(ref s, 2); |
|||
Vector256<float> f3 = Unsafe.Add(ref s, 3); |
|||
|
|||
Vector256<int> w0 = ConvertToInt32(f0, maxBytes); |
|||
Vector256<int> w1 = ConvertToInt32(f1, maxBytes); |
|||
Vector256<int> w2 = ConvertToInt32(f2, maxBytes); |
|||
Vector256<int> w3 = ConvertToInt32(f3, maxBytes); |
|||
|
|||
Vector256<short> u0 = Avx2.PackSignedSaturate(w0, w1); |
|||
Vector256<short> u1 = Avx2.PackSignedSaturate(w2, w3); |
|||
Vector256<byte> b = Avx2.PackUnsignedSaturate(u0, u1); |
|||
b = Avx2.PermuteVar8x32(b.AsInt32(), mask).AsByte(); |
|||
|
|||
Unsafe.Add(ref destBase, i) = b; |
|||
} |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
private static Vector256<int> ConvertToInt32(Vector256<float> vf, Vector256<float> scale) |
|||
{ |
|||
vf = Avx.Multiply(vf, scale); |
|||
return Avx.ConvertToVector256Int32(vf); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
#endif
|
|||
@ -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<byte> 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<byte> 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<byte> 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<byte> ShuffleMaskPad4Nx16 => new byte[] { 0, 1, 2, 0x80, 3, 4, 5, 0x80, 6, 7, 8, 0x80, 9, 10, 11, 0x80 }; |
|||
|
|||
private static ReadOnlySpan<byte> ShuffleMaskSlice4Nx16 => new byte[] { 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 0x80, 0x80, 0x80, 0x80 }; |
|||
|
|||
private static ReadOnlySpan<byte> 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<byte> 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 |
|||
}; |
|||
|
|||
/// <summary>
|
|||
/// Shuffle single-precision (32-bit) floating-point elements in <paramref name="source"/>
|
|||
/// using the control and store the results in <paramref name="dest"/>.
|
|||
/// </summary>
|
|||
/// <param name="source">The source span of floats.</param>
|
|||
/// <param name="dest">The destination span of floats.</param>
|
|||
/// <param name="control">The byte control.</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void Shuffle4Reduce( |
|||
ref ReadOnlySpan<float> source, |
|||
ref Span<float> dest, |
|||
byte control) |
|||
{ |
|||
if (Avx.IsSupported || Sse.IsSupported) |
|||
{ |
|||
int remainder = Avx.IsSupported |
|||
? Numerics.ModuloP2(source.Length, Vector256<float>.Count) |
|||
: Numerics.ModuloP2(source.Length, Vector128<float>.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); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Shuffle 8-bit integers within 128-bit lanes in <paramref name="source"/>
|
|||
/// using the control and store the results in <paramref name="dest"/>.
|
|||
/// </summary>
|
|||
/// <param name="source">The source span of bytes.</param>
|
|||
/// <param name="dest">The destination span of bytes.</param>
|
|||
/// <param name="control">The byte control.</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void Shuffle4Reduce( |
|||
ref ReadOnlySpan<byte> source, |
|||
ref Span<byte> dest, |
|||
byte control) |
|||
{ |
|||
if (Avx2.IsSupported || Ssse3.IsSupported) |
|||
{ |
|||
int remainder = Avx2.IsSupported |
|||
? Numerics.ModuloP2(source.Length, Vector256<byte>.Count) |
|||
: Numerics.ModuloP2(source.Length, Vector128<byte>.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); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Shuffles 8-bit integer triplets within 128-bit lanes in <paramref name="source"/>
|
|||
/// using the control and store the results in <paramref name="dest"/>.
|
|||
/// </summary>
|
|||
/// <param name="source">The source span of bytes.</param>
|
|||
/// <param name="dest">The destination span of bytes.</param>
|
|||
/// <param name="control">The byte control.</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void Shuffle3Reduce( |
|||
ref ReadOnlySpan<byte> source, |
|||
ref Span<byte> dest, |
|||
byte control) |
|||
{ |
|||
if (Ssse3.IsSupported) |
|||
{ |
|||
int remainder = source.Length % (Vector128<byte>.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); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Pads then shuffles 8-bit integers within 128-bit lanes in <paramref name="source"/>
|
|||
/// using the control and store the results in <paramref name="dest"/>.
|
|||
/// </summary>
|
|||
/// <param name="source">The source span of bytes.</param>
|
|||
/// <param name="dest">The destination span of bytes.</param>
|
|||
/// <param name="control">The byte control.</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void Pad3Shuffle4Reduce( |
|||
ref ReadOnlySpan<byte> source, |
|||
ref Span<byte> dest, |
|||
byte control) |
|||
{ |
|||
if (Ssse3.IsSupported) |
|||
{ |
|||
int remainder = source.Length % (Vector128<byte>.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); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Shuffles then slices 8-bit integers within 128-bit lanes in <paramref name="source"/>
|
|||
/// using the control and store the results in <paramref name="dest"/>.
|
|||
/// </summary>
|
|||
/// <param name="source">The source span of bytes.</param>
|
|||
/// <param name="dest">The destination span of bytes.</param>
|
|||
/// <param name="control">The byte control.</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void Shuffle4Slice3Reduce( |
|||
ref ReadOnlySpan<byte> source, |
|||
ref Span<byte> dest, |
|||
byte control) |
|||
{ |
|||
if (Ssse3.IsSupported) |
|||
{ |
|||
int remainder = source.Length % (Vector128<byte>.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<float> source, |
|||
Span<float> dest, |
|||
byte control) |
|||
{ |
|||
if (Avx.IsSupported) |
|||
{ |
|||
ref Vector256<float> sourceBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(source)); |
|||
|
|||
ref Vector256<float> destBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(dest)); |
|||
|
|||
int n = dest.Length / Vector256<float>.Count; |
|||
int m = Numerics.Modulo4(n); |
|||
int u = n - m; |
|||
|
|||
for (int i = 0; i < u; i += 4) |
|||
{ |
|||
ref Vector256<float> vd0 = ref Unsafe.Add(ref destBase, i); |
|||
ref Vector256<float> 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<float> sourceBase = |
|||
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(source)); |
|||
|
|||
ref Vector128<float> destBase = |
|||
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(dest)); |
|||
|
|||
int n = dest.Length / Vector128<float>.Count; |
|||
int m = Numerics.Modulo4(n); |
|||
int u = n - m; |
|||
|
|||
for (int i = 0; i < u; i += 4) |
|||
{ |
|||
ref Vector128<float> vd0 = ref Unsafe.Add(ref destBase, i); |
|||
ref Vector128<float> vs0 = ref Unsafe.Add(ref sourceBase, i); |
|||
|
|||
vd0 = Sse.Shuffle(vs0, vs0, control); |
|||
|
|||
Vector128<float> vs1 = Unsafe.Add(ref vs0, 1); |
|||
Unsafe.Add(ref vd0, 1) = Sse.Shuffle(vs1, vs1, control); |
|||
|
|||
Vector128<float> vs2 = Unsafe.Add(ref vs0, 2); |
|||
Unsafe.Add(ref vd0, 2) = Sse.Shuffle(vs2, vs2, control); |
|||
|
|||
Vector128<float> 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<float> vs = Unsafe.Add(ref sourceBase, i); |
|||
Unsafe.Add(ref destBase, i) = Sse.Shuffle(vs, vs, control); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
private static void Shuffle4( |
|||
ReadOnlySpan<byte> source, |
|||
Span<byte> 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<byte> bytes = stackalloc byte[Vector256<byte>.Count]; |
|||
Shuffle.MmShuffleSpan(ref bytes, control); |
|||
Vector256<byte> vshuffle = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(bytes)); |
|||
|
|||
ref Vector256<byte> sourceBase = |
|||
ref Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(source)); |
|||
|
|||
ref Vector256<byte> destBase = |
|||
ref Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(dest)); |
|||
|
|||
int n = dest.Length / Vector256<byte>.Count; |
|||
int m = Numerics.Modulo4(n); |
|||
int u = n - m; |
|||
|
|||
for (int i = 0; i < u; i += 4) |
|||
{ |
|||
ref Vector256<byte> vs0 = ref Unsafe.Add(ref sourceBase, i); |
|||
ref Vector256<byte> vd0 = ref Unsafe.Add(ref destBase, i); |
|||
|
|||
vd0 = Avx2.Shuffle(vs0, vshuffle); |
|||
Unsafe.Add(ref vd0, 1) = Avx2.Shuffle(Unsafe.Add(ref vs0, 1), vshuffle); |
|||
Unsafe.Add(ref vd0, 2) = Avx2.Shuffle(Unsafe.Add(ref vs0, 2), vshuffle); |
|||
Unsafe.Add(ref vd0, 3) = Avx2.Shuffle(Unsafe.Add(ref vs0, 3), vshuffle); |
|||
} |
|||
|
|||
if (m > 0) |
|||
{ |
|||
for (int i = u; i < n; i++) |
|||
{ |
|||
Unsafe.Add(ref destBase, i) = Avx2.Shuffle(Unsafe.Add(ref sourceBase, i), vshuffle); |
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
// Ssse3
|
|||
Span<byte> bytes = stackalloc byte[Vector128<byte>.Count]; |
|||
Shuffle.MmShuffleSpan(ref bytes, control); |
|||
Vector128<byte> vshuffle = Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(bytes)); |
|||
|
|||
ref Vector128<byte> sourceBase = |
|||
ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(source)); |
|||
|
|||
ref Vector128<byte> destBase = |
|||
ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(dest)); |
|||
|
|||
int n = dest.Length / Vector128<byte>.Count; |
|||
int m = Numerics.Modulo4(n); |
|||
int u = n - m; |
|||
|
|||
for (int i = 0; i < u; i += 4) |
|||
{ |
|||
ref Vector128<byte> vs0 = ref Unsafe.Add(ref sourceBase, i); |
|||
ref Vector128<byte> vd0 = ref Unsafe.Add(ref destBase, i); |
|||
|
|||
vd0 = Ssse3.Shuffle(vs0, vshuffle); |
|||
Unsafe.Add(ref vd0, 1) = Ssse3.Shuffle(Unsafe.Add(ref vs0, 1), vshuffle); |
|||
Unsafe.Add(ref vd0, 2) = Ssse3.Shuffle(Unsafe.Add(ref vs0, 2), vshuffle); |
|||
Unsafe.Add(ref vd0, 3) = Ssse3.Shuffle(Unsafe.Add(ref vs0, 3), vshuffle); |
|||
} |
|||
|
|||
if (m > 0) |
|||
{ |
|||
for (int i = u; i < n; i++) |
|||
{ |
|||
Unsafe.Add(ref destBase, i) = Ssse3.Shuffle(Unsafe.Add(ref sourceBase, i), vshuffle); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
private static void Shuffle3( |
|||
ReadOnlySpan<byte> source, |
|||
Span<byte> dest, |
|||
byte control) |
|||
{ |
|||
if (Ssse3.IsSupported) |
|||
{ |
|||
ref byte vmaskBase = ref MemoryMarshal.GetReference(ShuffleMaskPad4Nx16); |
|||
Vector128<byte> vmask = Unsafe.As<byte, Vector128<byte>>(ref vmaskBase); |
|||
ref byte vmaskoBase = ref MemoryMarshal.GetReference(ShuffleMaskSlice4Nx16); |
|||
Vector128<byte> vmasko = Unsafe.As<byte, Vector128<byte>>(ref vmaskoBase); |
|||
Vector128<byte> vmaske = Ssse3.AlignRight(vmasko, vmasko, 12); |
|||
|
|||
Span<byte> bytes = stackalloc byte[Vector128<byte>.Count]; |
|||
Shuffle.MmShuffleSpan(ref bytes, control); |
|||
Vector128<byte> vshuffle = Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(bytes)); |
|||
|
|||
ref Vector128<byte> sourceBase = |
|||
ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(source)); |
|||
|
|||
ref Vector128<byte> destBase = |
|||
ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(dest)); |
|||
|
|||
int n = source.Length / Vector128<byte>.Count; |
|||
|
|||
for (int i = 0; i < n; i += 3) |
|||
{ |
|||
ref Vector128<byte> vs = ref Unsafe.Add(ref sourceBase, i); |
|||
|
|||
Vector128<byte> v0 = vs; |
|||
Vector128<byte> v1 = Unsafe.Add(ref vs, 1); |
|||
Vector128<byte> v2 = Unsafe.Add(ref vs, 2); |
|||
Vector128<byte> v3 = Sse2.ShiftRightLogical128BitLane(v2, 4); |
|||
|
|||
v2 = Ssse3.AlignRight(v2, v1, 8); |
|||
v1 = Ssse3.AlignRight(v1, v0, 12); |
|||
|
|||
v0 = Ssse3.Shuffle(Ssse3.Shuffle(v0, vmask), vshuffle); |
|||
v1 = Ssse3.Shuffle(Ssse3.Shuffle(v1, vmask), vshuffle); |
|||
v2 = Ssse3.Shuffle(Ssse3.Shuffle(v2, vmask), vshuffle); |
|||
v3 = Ssse3.Shuffle(Ssse3.Shuffle(v3, vmask), vshuffle); |
|||
|
|||
v0 = Ssse3.Shuffle(v0, vmaske); |
|||
v1 = Ssse3.Shuffle(v1, vmasko); |
|||
v2 = Ssse3.Shuffle(v2, vmaske); |
|||
v3 = Ssse3.Shuffle(v3, vmasko); |
|||
|
|||
v0 = Ssse3.AlignRight(v1, v0, 4); |
|||
v3 = Ssse3.AlignRight(v3, v2, 12); |
|||
|
|||
v1 = Sse2.ShiftLeftLogical128BitLane(v1, 4); |
|||
v2 = Sse2.ShiftRightLogical128BitLane(v2, 4); |
|||
|
|||
v1 = Ssse3.AlignRight(v2, v1, 8); |
|||
|
|||
ref Vector128<byte> vd = ref Unsafe.Add(ref destBase, i); |
|||
|
|||
vd = v0; |
|||
Unsafe.Add(ref vd, 1) = v1; |
|||
Unsafe.Add(ref vd, 2) = v3; |
|||
} |
|||
} |
|||
} |
|||
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
private static void Pad3Shuffle4( |
|||
ReadOnlySpan<byte> source, |
|||
Span<byte> dest, |
|||
byte control) |
|||
{ |
|||
if (Ssse3.IsSupported) |
|||
{ |
|||
ref byte vmaskBase = ref MemoryMarshal.GetReference(ShuffleMaskPad4Nx16); |
|||
Vector128<byte> vmask = Unsafe.As<byte, Vector128<byte>>(ref vmaskBase); |
|||
Vector128<byte> vfill = Vector128.Create(0xff000000ff000000ul).AsByte(); |
|||
|
|||
Span<byte> bytes = stackalloc byte[Vector128<byte>.Count]; |
|||
Shuffle.MmShuffleSpan(ref bytes, control); |
|||
Vector128<byte> vshuffle = Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(bytes)); |
|||
|
|||
ref Vector128<byte> sourceBase = |
|||
ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(source)); |
|||
|
|||
ref Vector128<byte> destBase = |
|||
ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(dest)); |
|||
|
|||
int n = source.Length / Vector128<byte>.Count; |
|||
|
|||
for (int i = 0, j = 0; i < n; i += 3, j += 4) |
|||
{ |
|||
ref Vector128<byte> v0 = ref Unsafe.Add(ref sourceBase, i); |
|||
Vector128<byte> v1 = Unsafe.Add(ref v0, 1); |
|||
Vector128<byte> v2 = Unsafe.Add(ref v0, 2); |
|||
Vector128<byte> v3 = Sse2.ShiftRightLogical128BitLane(v2, 4); |
|||
|
|||
v2 = Ssse3.AlignRight(v2, v1, 8); |
|||
v1 = Ssse3.AlignRight(v1, v0, 12); |
|||
|
|||
ref Vector128<byte> vd = ref Unsafe.Add(ref destBase, j); |
|||
|
|||
vd = Ssse3.Shuffle(Sse2.Or(Ssse3.Shuffle(v0, vmask), vfill), vshuffle); |
|||
Unsafe.Add(ref vd, 1) = Ssse3.Shuffle(Sse2.Or(Ssse3.Shuffle(v1, vmask), vfill), vshuffle); |
|||
Unsafe.Add(ref vd, 2) = Ssse3.Shuffle(Sse2.Or(Ssse3.Shuffle(v2, vmask), vfill), vshuffle); |
|||
Unsafe.Add(ref vd, 3) = Ssse3.Shuffle(Sse2.Or(Ssse3.Shuffle(v3, vmask), vfill), vshuffle); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
private static void Shuffle4Slice3( |
|||
ReadOnlySpan<byte> source, |
|||
Span<byte> dest, |
|||
byte control) |
|||
{ |
|||
if (Ssse3.IsSupported) |
|||
{ |
|||
ref byte vmaskoBase = ref MemoryMarshal.GetReference(ShuffleMaskSlice4Nx16); |
|||
Vector128<byte> vmasko = Unsafe.As<byte, Vector128<byte>>(ref vmaskoBase); |
|||
Vector128<byte> vmaske = Ssse3.AlignRight(vmasko, vmasko, 12); |
|||
|
|||
Span<byte> bytes = stackalloc byte[Vector128<byte>.Count]; |
|||
Shuffle.MmShuffleSpan(ref bytes, control); |
|||
Vector128<byte> vshuffle = Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(bytes)); |
|||
|
|||
ref Vector128<byte> sourceBase = |
|||
ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(source)); |
|||
|
|||
ref Vector128<byte> destBase = |
|||
ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(dest)); |
|||
|
|||
int n = source.Length / Vector128<byte>.Count; |
|||
|
|||
for (int i = 0, j = 0; i < n; i += 4, j += 3) |
|||
{ |
|||
ref Vector128<byte> vs = ref Unsafe.Add(ref sourceBase, i); |
|||
|
|||
Vector128<byte> v0 = vs; |
|||
Vector128<byte> v1 = Unsafe.Add(ref vs, 1); |
|||
Vector128<byte> v2 = Unsafe.Add(ref vs, 2); |
|||
Vector128<byte> v3 = Unsafe.Add(ref vs, 3); |
|||
|
|||
v0 = Ssse3.Shuffle(Ssse3.Shuffle(v0, vshuffle), vmaske); |
|||
v1 = Ssse3.Shuffle(Ssse3.Shuffle(v1, vshuffle), vmasko); |
|||
v2 = Ssse3.Shuffle(Ssse3.Shuffle(v2, vshuffle), vmaske); |
|||
v3 = Ssse3.Shuffle(Ssse3.Shuffle(v3, vshuffle), vmasko); |
|||
|
|||
v0 = Ssse3.AlignRight(v1, v0, 4); |
|||
v3 = Ssse3.AlignRight(v3, v2, 12); |
|||
|
|||
v1 = Sse2.ShiftLeftLogical128BitLane(v1, 4); |
|||
v2 = Sse2.ShiftRightLogical128BitLane(v2, 4); |
|||
|
|||
v1 = Ssse3.AlignRight(v2, v1, 8); |
|||
|
|||
ref Vector128<byte> vd = ref Unsafe.Add(ref destBase, j); |
|||
|
|||
vd = v0; |
|||
Unsafe.Add(ref vd, 1) = v1; |
|||
Unsafe.Add(ref vd, 2) = v3; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Performs a multiplication and an addition of the <see cref="Vector256{T}"/>.
|
|||
/// </summary>
|
|||
/// <param name="va">The vector to add to the intermediate result.</param>
|
|||
/// <param name="vm0">The first vector to multiply.</param>
|
|||
/// <param name="vm1">The second vector to multiply.</param>
|
|||
/// <returns>The <see cref="Vector256{T}"/>.</returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static Vector256<float> MultiplyAdd( |
|||
in Vector256<float> va, |
|||
in Vector256<float> vm0, |
|||
in Vector256<float> vm1) |
|||
{ |
|||
if (Fma.IsSupported) |
|||
{ |
|||
return Fma.MultiplyAdd(vm1, vm0, va); |
|||
} |
|||
else |
|||
{ |
|||
return Avx.Add(Avx.Multiply(vm0, vm1), va); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// <see cref="ByteToNormalizedFloat"/> as many elements as possible, slicing them down (keeping the remainder).
|
|||
/// </summary>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
internal static void ByteToNormalizedFloatReduce( |
|||
ref ReadOnlySpan<byte> source, |
|||
ref Span<float> dest) |
|||
{ |
|||
DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); |
|||
|
|||
if (Avx2.IsSupported || Sse2.IsSupported) |
|||
{ |
|||
int remainder; |
|||
if (Avx2.IsSupported) |
|||
{ |
|||
remainder = Numerics.ModuloP2(source.Length, Vector256<byte>.Count); |
|||
} |
|||
else |
|||
{ |
|||
remainder = Numerics.ModuloP2(source.Length, Vector128<byte>.Count); |
|||
} |
|||
|
|||
int adjustedCount = source.Length - remainder; |
|||
|
|||
if (adjustedCount > 0) |
|||
{ |
|||
ByteToNormalizedFloat(source.Slice(0, adjustedCount), dest.Slice(0, adjustedCount)); |
|||
|
|||
source = source.Slice(adjustedCount); |
|||
dest = dest.Slice(adjustedCount); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Implementation <see cref="SimdUtils.ByteToNormalizedFloat"/>, which is faster on new RyuJIT runtime.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Implementation is based on MagicScaler code:
|
|||
/// https://github.com/saucecontrol/PhotoSauce/blob/b5811908041200488aa18fdfd17df5fc457415dc/src/MagicScaler/Magic/Processors/ConvertersFloat.cs#L80-L182
|
|||
/// </remarks>
|
|||
internal static unsafe void ByteToNormalizedFloat( |
|||
ReadOnlySpan<byte> source, |
|||
Span<float> dest) |
|||
{ |
|||
if (Avx2.IsSupported) |
|||
{ |
|||
VerifySpanInput(source, dest, Vector256<byte>.Count); |
|||
|
|||
int n = dest.Length / Vector256<byte>.Count; |
|||
|
|||
byte* sourceBase = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(source)); |
|||
|
|||
ref Vector256<float> destBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(dest)); |
|||
|
|||
var scale = Vector256.Create(1 / (float)byte.MaxValue); |
|||
|
|||
for (int i = 0; i < n; i++) |
|||
{ |
|||
int si = Vector256<byte>.Count * i; |
|||
Vector256<int> i0 = Avx2.ConvertToVector256Int32(sourceBase + si); |
|||
Vector256<int> i1 = Avx2.ConvertToVector256Int32(sourceBase + si + Vector256<int>.Count); |
|||
Vector256<int> i2 = Avx2.ConvertToVector256Int32(sourceBase + si + (Vector256<int>.Count * 2)); |
|||
Vector256<int> i3 = Avx2.ConvertToVector256Int32(sourceBase + si + (Vector256<int>.Count * 3)); |
|||
|
|||
Vector256<float> f0 = Avx.Multiply(scale, Avx.ConvertToVector256Single(i0)); |
|||
Vector256<float> f1 = Avx.Multiply(scale, Avx.ConvertToVector256Single(i1)); |
|||
Vector256<float> f2 = Avx.Multiply(scale, Avx.ConvertToVector256Single(i2)); |
|||
Vector256<float> f3 = Avx.Multiply(scale, Avx.ConvertToVector256Single(i3)); |
|||
|
|||
ref Vector256<float> d = ref Unsafe.Add(ref destBase, i * 4); |
|||
|
|||
d = f0; |
|||
Unsafe.Add(ref d, 1) = f1; |
|||
Unsafe.Add(ref d, 2) = f2; |
|||
Unsafe.Add(ref d, 3) = f3; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
// Sse
|
|||
VerifySpanInput(source, dest, Vector128<byte>.Count); |
|||
|
|||
int n = dest.Length / Vector128<byte>.Count; |
|||
|
|||
byte* sourceBase = (byte*)Unsafe.AsPointer(ref MemoryMarshal.GetReference(source)); |
|||
|
|||
ref Vector128<float> destBase = |
|||
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(dest)); |
|||
|
|||
var scale = Vector128.Create(1 / (float)byte.MaxValue); |
|||
Vector128<byte> zero = Vector128<byte>.Zero; |
|||
|
|||
for (int i = 0; i < n; i++) |
|||
{ |
|||
int si = Vector128<byte>.Count * i; |
|||
|
|||
Vector128<int> i0, i1, i2, i3; |
|||
if (Sse41.IsSupported) |
|||
{ |
|||
i0 = Sse41.ConvertToVector128Int32(sourceBase + si); |
|||
i1 = Sse41.ConvertToVector128Int32(sourceBase + si + Vector128<int>.Count); |
|||
i2 = Sse41.ConvertToVector128Int32(sourceBase + si + (Vector128<int>.Count * 2)); |
|||
i3 = Sse41.ConvertToVector128Int32(sourceBase + si + (Vector128<int>.Count * 3)); |
|||
} |
|||
else |
|||
{ |
|||
Vector128<byte> b = Sse2.LoadVector128(sourceBase + si); |
|||
Vector128<short> s0 = Sse2.UnpackLow(b, zero).AsInt16(); |
|||
Vector128<short> s1 = Sse2.UnpackHigh(b, zero).AsInt16(); |
|||
|
|||
i0 = Sse2.UnpackLow(s0, zero.AsInt16()).AsInt32(); |
|||
i1 = Sse2.UnpackHigh(s0, zero.AsInt16()).AsInt32(); |
|||
i2 = Sse2.UnpackLow(s1, zero.AsInt16()).AsInt32(); |
|||
i3 = Sse2.UnpackHigh(s1, zero.AsInt16()).AsInt32(); |
|||
} |
|||
|
|||
Vector128<float> f0 = Sse.Multiply(scale, Sse2.ConvertToVector128Single(i0)); |
|||
Vector128<float> f1 = Sse.Multiply(scale, Sse2.ConvertToVector128Single(i1)); |
|||
Vector128<float> f2 = Sse.Multiply(scale, Sse2.ConvertToVector128Single(i2)); |
|||
Vector128<float> f3 = Sse.Multiply(scale, Sse2.ConvertToVector128Single(i3)); |
|||
|
|||
ref Vector128<float> d = ref Unsafe.Add(ref destBase, i * 4); |
|||
|
|||
d = f0; |
|||
Unsafe.Add(ref d, 1) = f1; |
|||
Unsafe.Add(ref d, 2) = f2; |
|||
Unsafe.Add(ref d, 3) = f3; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// <see cref="NormalizedFloatToByteSaturate"/> as many elements as possible, slicing them down (keeping the remainder).
|
|||
/// </summary>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
internal static void NormalizedFloatToByteSaturateReduce( |
|||
ref ReadOnlySpan<float> source, |
|||
ref Span<byte> dest) |
|||
{ |
|||
DebugGuard.IsTrue(source.Length == dest.Length, nameof(source), "Input spans must be of same length!"); |
|||
|
|||
if (Avx2.IsSupported || Sse2.IsSupported) |
|||
{ |
|||
int remainder; |
|||
if (Avx2.IsSupported) |
|||
{ |
|||
remainder = Numerics.ModuloP2(source.Length, Vector256<byte>.Count); |
|||
} |
|||
else |
|||
{ |
|||
remainder = Numerics.ModuloP2(source.Length, Vector128<byte>.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); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Implementation of <see cref="SimdUtils.NormalizedFloatToByteSaturate"/>, which is faster on new .NET runtime.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// Implementation is based on MagicScaler code:
|
|||
/// https://github.com/saucecontrol/PhotoSauce/blob/b5811908041200488aa18fdfd17df5fc457415dc/src/MagicScaler/Magic/Processors/ConvertersFloat.cs#L541-L622
|
|||
/// </remarks>
|
|||
internal static void NormalizedFloatToByteSaturate( |
|||
ReadOnlySpan<float> source, |
|||
Span<byte> dest) |
|||
{ |
|||
if (Avx2.IsSupported) |
|||
{ |
|||
VerifySpanInput(source, dest, Vector256<byte>.Count); |
|||
|
|||
int n = dest.Length / Vector256<byte>.Count; |
|||
|
|||
ref Vector256<float> sourceBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(source)); |
|||
|
|||
ref Vector256<byte> destBase = |
|||
ref Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(dest)); |
|||
|
|||
var scale = Vector256.Create((float)byte.MaxValue); |
|||
ref byte maskBase = ref MemoryMarshal.GetReference(PermuteMaskDeinterleave8x32); |
|||
Vector256<int> mask = Unsafe.As<byte, Vector256<int>>(ref maskBase); |
|||
|
|||
for (int i = 0; i < n; i++) |
|||
{ |
|||
ref Vector256<float> s = ref Unsafe.Add(ref sourceBase, i * 4); |
|||
|
|||
Vector256<float> f0 = Avx.Multiply(scale, s); |
|||
Vector256<float> f1 = Avx.Multiply(scale, Unsafe.Add(ref s, 1)); |
|||
Vector256<float> f2 = Avx.Multiply(scale, Unsafe.Add(ref s, 2)); |
|||
Vector256<float> f3 = Avx.Multiply(scale, Unsafe.Add(ref s, 3)); |
|||
|
|||
Vector256<int> w0 = Avx.ConvertToVector256Int32(f0); |
|||
Vector256<int> w1 = Avx.ConvertToVector256Int32(f1); |
|||
Vector256<int> w2 = Avx.ConvertToVector256Int32(f2); |
|||
Vector256<int> w3 = Avx.ConvertToVector256Int32(f3); |
|||
|
|||
Vector256<short> u0 = Avx2.PackSignedSaturate(w0, w1); |
|||
Vector256<short> u1 = Avx2.PackSignedSaturate(w2, w3); |
|||
Vector256<byte> b = Avx2.PackUnsignedSaturate(u0, u1); |
|||
b = Avx2.PermuteVar8x32(b.AsInt32(), mask).AsByte(); |
|||
|
|||
Unsafe.Add(ref destBase, i) = b; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
// Sse
|
|||
VerifySpanInput(source, dest, Vector128<byte>.Count); |
|||
|
|||
int n = dest.Length / Vector128<byte>.Count; |
|||
|
|||
ref Vector128<float> sourceBase = |
|||
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(source)); |
|||
|
|||
ref Vector128<byte> destBase = |
|||
ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(dest)); |
|||
|
|||
var scale = Vector128.Create((float)byte.MaxValue); |
|||
|
|||
for (int i = 0; i < n; i++) |
|||
{ |
|||
ref Vector128<float> s = ref Unsafe.Add(ref sourceBase, i * 4); |
|||
|
|||
Vector128<float> f0 = Sse.Multiply(scale, s); |
|||
Vector128<float> f1 = Sse.Multiply(scale, Unsafe.Add(ref s, 1)); |
|||
Vector128<float> f2 = Sse.Multiply(scale, Unsafe.Add(ref s, 2)); |
|||
Vector128<float> f3 = Sse.Multiply(scale, Unsafe.Add(ref s, 3)); |
|||
|
|||
Vector128<int> w0 = Sse2.ConvertToVector128Int32(f0); |
|||
Vector128<int> w1 = Sse2.ConvertToVector128Int32(f1); |
|||
Vector128<int> w2 = Sse2.ConvertToVector128Int32(f2); |
|||
Vector128<int> w3 = Sse2.ConvertToVector128Int32(f3); |
|||
|
|||
Vector128<short> u0 = Sse2.PackSignedSaturate(w0, w1); |
|||
Vector128<short> u1 = Sse2.PackSignedSaturate(w2, w3); |
|||
|
|||
Unsafe.Add(ref destBase, i) = Sse2.PackUnsignedSaturate(u0, u1); |
|||
} |
|||
} |
|||
} |
|||
|
|||
internal static void PackFromRgbPlanesAvx2Reduce( |
|||
ref ReadOnlySpan<byte> redChannel, |
|||
ref ReadOnlySpan<byte> greenChannel, |
|||
ref ReadOnlySpan<byte> blueChannel, |
|||
ref Span<Rgb24> destination) |
|||
{ |
|||
ref Vector256<byte> rBase = ref Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(redChannel)); |
|||
ref Vector256<byte> gBase = ref Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(greenChannel)); |
|||
ref Vector256<byte> bBase = ref Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(blueChannel)); |
|||
ref byte dBase = ref Unsafe.As<Rgb24, byte>(ref MemoryMarshal.GetReference(destination)); |
|||
|
|||
int count = redChannel.Length / Vector256<byte>.Count; |
|||
|
|||
ref byte control1Bytes = ref MemoryMarshal.GetReference(SimdUtils.HwIntrinsics.PermuteMaskEvenOdd8x32); |
|||
Vector256<uint> control1 = Unsafe.As<byte, Vector256<uint>>(ref control1Bytes); |
|||
|
|||
ref byte control2Bytes = ref MemoryMarshal.GetReference(PermuteMaskShiftAlpha8x32); |
|||
Vector256<uint> control2 = Unsafe.As<byte, Vector256<uint>>(ref control2Bytes); |
|||
|
|||
Vector256<byte> a = Vector256.Create((byte)255); |
|||
|
|||
Vector256<byte> shuffleAlpha = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(ShuffleMaskShiftAlpha)); |
|||
|
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
Vector256<byte> r0 = Unsafe.Add(ref rBase, i); |
|||
Vector256<byte> g0 = Unsafe.Add(ref gBase, i); |
|||
Vector256<byte> b0 = Unsafe.Add(ref bBase, i); |
|||
|
|||
r0 = Avx2.PermuteVar8x32(r0.AsUInt32(), control1).AsByte(); |
|||
g0 = Avx2.PermuteVar8x32(g0.AsUInt32(), control1).AsByte(); |
|||
b0 = Avx2.PermuteVar8x32(b0.AsUInt32(), control1).AsByte(); |
|||
|
|||
Vector256<byte> rg = Avx2.UnpackLow(r0, g0); |
|||
Vector256<byte> b1 = Avx2.UnpackLow(b0, a); |
|||
|
|||
Vector256<byte> rgb1 = Avx2.UnpackLow(rg.AsUInt16(), b1.AsUInt16()).AsByte(); |
|||
Vector256<byte> rgb2 = Avx2.UnpackHigh(rg.AsUInt16(), b1.AsUInt16()).AsByte(); |
|||
|
|||
rg = Avx2.UnpackHigh(r0, g0); |
|||
b1 = Avx2.UnpackHigh(b0, a); |
|||
|
|||
Vector256<byte> rgb3 = Avx2.UnpackLow(rg.AsUInt16(), b1.AsUInt16()).AsByte(); |
|||
Vector256<byte> rgb4 = Avx2.UnpackHigh(rg.AsUInt16(), b1.AsUInt16()).AsByte(); |
|||
|
|||
rgb1 = Avx2.Shuffle(rgb1, shuffleAlpha); |
|||
rgb2 = Avx2.Shuffle(rgb2, shuffleAlpha); |
|||
rgb3 = Avx2.Shuffle(rgb3, shuffleAlpha); |
|||
rgb4 = Avx2.Shuffle(rgb4, shuffleAlpha); |
|||
|
|||
rgb1 = Avx2.PermuteVar8x32(rgb1.AsUInt32(), control2).AsByte(); |
|||
rgb2 = Avx2.PermuteVar8x32(rgb2.AsUInt32(), control2).AsByte(); |
|||
rgb3 = Avx2.PermuteVar8x32(rgb3.AsUInt32(), control2).AsByte(); |
|||
rgb4 = Avx2.PermuteVar8x32(rgb4.AsUInt32(), control2).AsByte(); |
|||
|
|||
ref byte d1 = ref Unsafe.Add(ref dBase, 24 * 4 * i); |
|||
ref byte d2 = ref Unsafe.Add(ref d1, 24); |
|||
ref byte d3 = ref Unsafe.Add(ref d2, 24); |
|||
ref byte d4 = ref Unsafe.Add(ref d3, 24); |
|||
|
|||
Unsafe.As<byte, Vector256<byte>>(ref d1) = rgb1; |
|||
Unsafe.As<byte, Vector256<byte>>(ref d2) = rgb2; |
|||
Unsafe.As<byte, Vector256<byte>>(ref d3) = rgb3; |
|||
Unsafe.As<byte, Vector256<byte>>(ref d4) = rgb4; |
|||
} |
|||
|
|||
int slice = count * Vector256<byte>.Count; |
|||
redChannel = redChannel.Slice(slice); |
|||
greenChannel = greenChannel.Slice(slice); |
|||
blueChannel = blueChannel.Slice(slice); |
|||
destination = destination.Slice(slice); |
|||
} |
|||
|
|||
internal static void PackFromRgbPlanesAvx2Reduce( |
|||
ref ReadOnlySpan<byte> redChannel, |
|||
ref ReadOnlySpan<byte> greenChannel, |
|||
ref ReadOnlySpan<byte> blueChannel, |
|||
ref Span<Rgba32> destination) |
|||
{ |
|||
ref Vector256<byte> rBase = ref Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(redChannel)); |
|||
ref Vector256<byte> gBase = ref Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(greenChannel)); |
|||
ref Vector256<byte> bBase = ref Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(blueChannel)); |
|||
ref Vector256<byte> dBase = ref Unsafe.As<Rgba32, Vector256<byte>>(ref MemoryMarshal.GetReference(destination)); |
|||
|
|||
int count = redChannel.Length / Vector256<byte>.Count; |
|||
|
|||
ref byte control1Bytes = ref MemoryMarshal.GetReference(SimdUtils.HwIntrinsics.PermuteMaskEvenOdd8x32); |
|||
Vector256<uint> control1 = Unsafe.As<byte, Vector256<uint>>(ref control1Bytes); |
|||
|
|||
ref byte control2Bytes = ref MemoryMarshal.GetReference(PermuteMaskShiftAlpha8x32); |
|||
Vector256<uint> control2 = Unsafe.As<byte, Vector256<uint>>(ref control2Bytes); |
|||
|
|||
Vector256<byte> a = Vector256.Create((byte)255); |
|||
|
|||
Vector256<byte> shuffleAlpha = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(ShuffleMaskShiftAlpha)); |
|||
|
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
Vector256<byte> r0 = Unsafe.Add(ref rBase, i); |
|||
Vector256<byte> g0 = Unsafe.Add(ref gBase, i); |
|||
Vector256<byte> b0 = Unsafe.Add(ref bBase, i); |
|||
|
|||
r0 = Avx2.PermuteVar8x32(r0.AsUInt32(), control1).AsByte(); |
|||
g0 = Avx2.PermuteVar8x32(g0.AsUInt32(), control1).AsByte(); |
|||
b0 = Avx2.PermuteVar8x32(b0.AsUInt32(), control1).AsByte(); |
|||
|
|||
Vector256<byte> rg = Avx2.UnpackLow(r0, g0); |
|||
Vector256<byte> b1 = Avx2.UnpackLow(b0, a); |
|||
|
|||
Vector256<byte> rgb1 = Avx2.UnpackLow(rg.AsUInt16(), b1.AsUInt16()).AsByte(); |
|||
Vector256<byte> rgb2 = Avx2.UnpackHigh(rg.AsUInt16(), b1.AsUInt16()).AsByte(); |
|||
|
|||
rg = Avx2.UnpackHigh(r0, g0); |
|||
b1 = Avx2.UnpackHigh(b0, a); |
|||
|
|||
Vector256<byte> rgb3 = Avx2.UnpackLow(rg.AsUInt16(), b1.AsUInt16()).AsByte(); |
|||
Vector256<byte> rgb4 = Avx2.UnpackHigh(rg.AsUInt16(), b1.AsUInt16()).AsByte(); |
|||
|
|||
ref Vector256<byte> d0 = ref Unsafe.Add(ref dBase, i * 4); |
|||
d0 = rgb1; |
|||
Unsafe.Add(ref d0, 1) = rgb2; |
|||
Unsafe.Add(ref d0, 2) = rgb3; |
|||
Unsafe.Add(ref d0, 3) = rgb4; |
|||
} |
|||
|
|||
int slice = count * Vector256<byte>.Count; |
|||
redChannel = redChannel.Slice(slice); |
|||
greenChannel = greenChannel.Slice(slice); |
|||
blueChannel = blueChannel.Slice(slice); |
|||
destination = destination.Slice(slice); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
#endif
|
|||
@ -0,0 +1,206 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
using System.Runtime.Intrinsics; |
|||
using System.Runtime.Intrinsics.X86; |
|||
#endif
|
|||
|
|||
namespace SixLabors.ImageSharp |
|||
{ |
|||
internal static partial class SimdUtils |
|||
{ |
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
internal static void PackFromRgbPlanes( |
|||
Configuration configuration, |
|||
ReadOnlySpan<byte> redChannel, |
|||
ReadOnlySpan<byte> greenChannel, |
|||
ReadOnlySpan<byte> blueChannel, |
|||
Span<Rgb24> destination) |
|||
{ |
|||
DebugGuard.IsTrue(greenChannel.Length == redChannel.Length, nameof(greenChannel), "Channels must be of same size!"); |
|||
DebugGuard.IsTrue(blueChannel.Length == redChannel.Length, nameof(blueChannel), "Channels must be of same size!"); |
|||
DebugGuard.IsTrue(destination.Length > redChannel.Length + 2, nameof(destination), "'destination' must contain a padding of 3 elements!"); |
|||
|
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
if (Avx2.IsSupported) |
|||
{ |
|||
HwIntrinsics.PackFromRgbPlanesAvx2Reduce(ref redChannel, ref greenChannel, ref blueChannel, ref destination); |
|||
} |
|||
else |
|||
#endif
|
|||
{ |
|||
PackFromRgbPlanesScalarBatchedReduce(ref redChannel, ref greenChannel, ref blueChannel, ref destination); |
|||
} |
|||
|
|||
PackFromRgbPlanesRemainder(redChannel, greenChannel, blueChannel, destination); |
|||
} |
|||
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
internal static void PackFromRgbPlanes( |
|||
Configuration configuration, |
|||
ReadOnlySpan<byte> redChannel, |
|||
ReadOnlySpan<byte> greenChannel, |
|||
ReadOnlySpan<byte> blueChannel, |
|||
Span<Rgba32> destination) |
|||
{ |
|||
DebugGuard.IsTrue(greenChannel.Length == redChannel.Length, nameof(greenChannel), "Channels must be of same size!"); |
|||
DebugGuard.IsTrue(blueChannel.Length == redChannel.Length, nameof(blueChannel), "Channels must be of same size!"); |
|||
DebugGuard.IsTrue(destination.Length > redChannel.Length, nameof(destination), "'destination' span should not be shorter than the source channels!"); |
|||
|
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
if (Avx2.IsSupported) |
|||
{ |
|||
HwIntrinsics.PackFromRgbPlanesAvx2Reduce(ref redChannel, ref greenChannel, ref blueChannel, ref destination); |
|||
} |
|||
else |
|||
#endif
|
|||
{ |
|||
PackFromRgbPlanesScalarBatchedReduce(ref redChannel, ref greenChannel, ref blueChannel, ref destination); |
|||
} |
|||
|
|||
PackFromRgbPlanesRemainder(redChannel, greenChannel, blueChannel, destination); |
|||
} |
|||
|
|||
private static void PackFromRgbPlanesScalarBatchedReduce( |
|||
ref ReadOnlySpan<byte> redChannel, |
|||
ref ReadOnlySpan<byte> greenChannel, |
|||
ref ReadOnlySpan<byte> blueChannel, |
|||
ref Span<Rgb24> destination) |
|||
{ |
|||
ref ByteTuple4 r = ref Unsafe.As<byte, ByteTuple4>(ref MemoryMarshal.GetReference(redChannel)); |
|||
ref ByteTuple4 g = ref Unsafe.As<byte, ByteTuple4>(ref MemoryMarshal.GetReference(greenChannel)); |
|||
ref ByteTuple4 b = ref Unsafe.As<byte, ByteTuple4>(ref MemoryMarshal.GetReference(blueChannel)); |
|||
ref Rgb24 rgb = ref MemoryMarshal.GetReference(destination); |
|||
|
|||
int count = redChannel.Length / 4; |
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
ref Rgb24 d0 = ref Unsafe.Add(ref rgb, i * 4); |
|||
ref Rgb24 d1 = ref Unsafe.Add(ref d0, 1); |
|||
ref Rgb24 d2 = ref Unsafe.Add(ref d0, 2); |
|||
ref Rgb24 d3 = ref Unsafe.Add(ref d0, 3); |
|||
|
|||
ref ByteTuple4 rr = ref Unsafe.Add(ref r, i); |
|||
ref ByteTuple4 gg = ref Unsafe.Add(ref g, i); |
|||
ref ByteTuple4 bb = ref Unsafe.Add(ref b, i); |
|||
|
|||
d0.R = rr.V0; |
|||
d0.G = gg.V0; |
|||
d0.B = bb.V0; |
|||
|
|||
d1.R = rr.V1; |
|||
d1.G = gg.V1; |
|||
d1.B = bb.V1; |
|||
|
|||
d2.R = rr.V2; |
|||
d2.G = gg.V2; |
|||
d2.B = bb.V2; |
|||
|
|||
d3.R = rr.V3; |
|||
d3.G = gg.V3; |
|||
d3.B = bb.V3; |
|||
} |
|||
|
|||
int finished = count * 4; |
|||
redChannel = redChannel.Slice(finished); |
|||
greenChannel = greenChannel.Slice(finished); |
|||
blueChannel = blueChannel.Slice(finished); |
|||
destination = destination.Slice(finished); |
|||
} |
|||
|
|||
private static void PackFromRgbPlanesScalarBatchedReduce( |
|||
ref ReadOnlySpan<byte> redChannel, |
|||
ref ReadOnlySpan<byte> greenChannel, |
|||
ref ReadOnlySpan<byte> blueChannel, |
|||
ref Span<Rgba32> destination) |
|||
{ |
|||
ref ByteTuple4 r = ref Unsafe.As<byte, ByteTuple4>(ref MemoryMarshal.GetReference(redChannel)); |
|||
ref ByteTuple4 g = ref Unsafe.As<byte, ByteTuple4>(ref MemoryMarshal.GetReference(greenChannel)); |
|||
ref ByteTuple4 b = ref Unsafe.As<byte, ByteTuple4>(ref MemoryMarshal.GetReference(blueChannel)); |
|||
ref Rgba32 rgb = ref MemoryMarshal.GetReference(destination); |
|||
|
|||
int count = redChannel.Length / 4; |
|||
destination.Fill(new Rgba32(0, 0, 0, 255)); |
|||
for (int i = 0; i < count; i++) |
|||
{ |
|||
ref Rgba32 d0 = ref Unsafe.Add(ref rgb, i * 4); |
|||
ref Rgba32 d1 = ref Unsafe.Add(ref d0, 1); |
|||
ref Rgba32 d2 = ref Unsafe.Add(ref d0, 2); |
|||
ref Rgba32 d3 = ref Unsafe.Add(ref d0, 3); |
|||
|
|||
ref ByteTuple4 rr = ref Unsafe.Add(ref r, i); |
|||
ref ByteTuple4 gg = ref Unsafe.Add(ref g, i); |
|||
ref ByteTuple4 bb = ref Unsafe.Add(ref b, i); |
|||
|
|||
d0.R = rr.V0; |
|||
d0.G = gg.V0; |
|||
d0.B = bb.V0; |
|||
|
|||
d1.R = rr.V1; |
|||
d1.G = gg.V1; |
|||
d1.B = bb.V1; |
|||
|
|||
d2.R = rr.V2; |
|||
d2.G = gg.V2; |
|||
d2.B = bb.V2; |
|||
|
|||
d3.R = rr.V3; |
|||
d3.G = gg.V3; |
|||
d3.B = bb.V3; |
|||
} |
|||
|
|||
int finished = count * 4; |
|||
redChannel = redChannel.Slice(finished); |
|||
greenChannel = greenChannel.Slice(finished); |
|||
blueChannel = blueChannel.Slice(finished); |
|||
destination = destination.Slice(finished); |
|||
} |
|||
|
|||
private static void PackFromRgbPlanesRemainder( |
|||
ReadOnlySpan<byte> redChannel, |
|||
ReadOnlySpan<byte> greenChannel, |
|||
ReadOnlySpan<byte> blueChannel, |
|||
Span<Rgb24> destination) |
|||
{ |
|||
ref byte r = ref MemoryMarshal.GetReference(redChannel); |
|||
ref byte g = ref MemoryMarshal.GetReference(greenChannel); |
|||
ref byte b = ref MemoryMarshal.GetReference(blueChannel); |
|||
ref Rgb24 rgb = ref MemoryMarshal.GetReference(destination); |
|||
|
|||
for (int i = 0; i < destination.Length; i++) |
|||
{ |
|||
ref Rgb24 d = ref Unsafe.Add(ref rgb, i); |
|||
d.R = Unsafe.Add(ref r, i); |
|||
d.G = Unsafe.Add(ref g, i); |
|||
d.B = Unsafe.Add(ref b, i); |
|||
} |
|||
} |
|||
|
|||
private static void PackFromRgbPlanesRemainder( |
|||
ReadOnlySpan<byte> redChannel, |
|||
ReadOnlySpan<byte> greenChannel, |
|||
ReadOnlySpan<byte> blueChannel, |
|||
Span<Rgba32> destination) |
|||
{ |
|||
ref byte r = ref MemoryMarshal.GetReference(redChannel); |
|||
ref byte g = ref MemoryMarshal.GetReference(greenChannel); |
|||
ref byte b = ref MemoryMarshal.GetReference(blueChannel); |
|||
ref Rgba32 rgba = ref MemoryMarshal.GetReference(destination); |
|||
|
|||
for (int i = 0; i < destination.Length; i++) |
|||
{ |
|||
ref Rgba32 d = ref Unsafe.Add(ref rgba, i); |
|||
d.R = Unsafe.Add(ref r, i); |
|||
d.G = Unsafe.Add(ref g, i); |
|||
d.B = Unsafe.Add(ref b, i); |
|||
d.A = 255; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,275 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Diagnostics; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace SixLabors.ImageSharp |
|||
{ |
|||
internal static partial class SimdUtils |
|||
{ |
|||
/// <summary>
|
|||
/// Shuffle single-precision (32-bit) floating-point elements in <paramref name="source"/>
|
|||
/// using the control and store the results in <paramref name="dest"/>.
|
|||
/// </summary>
|
|||
/// <param name="source">The source span of floats.</param>
|
|||
/// <param name="dest">The destination span of floats.</param>
|
|||
/// <param name="control">The byte control.</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void Shuffle4( |
|||
ReadOnlySpan<float> source, |
|||
Span<float> dest, |
|||
byte control) |
|||
{ |
|||
VerifyShuffle4SpanInput(source, dest); |
|||
|
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
HwIntrinsics.Shuffle4Reduce(ref source, ref dest, control); |
|||
#endif
|
|||
|
|||
// Deal with the remainder:
|
|||
if (source.Length > 0) |
|||
{ |
|||
Shuffle4Remainder(source, dest, control); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Shuffle 8-bit integers within 128-bit lanes in <paramref name="source"/>
|
|||
/// using the control and store the results in <paramref name="dest"/>.
|
|||
/// </summary>
|
|||
/// <param name="source">The source span of bytes.</param>
|
|||
/// <param name="dest">The destination span of bytes.</param>
|
|||
/// <param name="shuffle">The type of shuffle to perform.</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void Shuffle4<TShuffle>( |
|||
ReadOnlySpan<byte> source, |
|||
Span<byte> dest, |
|||
TShuffle shuffle) |
|||
where TShuffle : struct, IShuffle4 |
|||
{ |
|||
VerifyShuffle4SpanInput(source, dest); |
|||
|
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
HwIntrinsics.Shuffle4Reduce(ref source, ref dest, shuffle.Control); |
|||
#endif
|
|||
|
|||
// Deal with the remainder:
|
|||
if (source.Length > 0) |
|||
{ |
|||
shuffle.RunFallbackShuffle(source, dest); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Shuffle 8-bit integer triplets within 128-bit lanes in <paramref name="source"/>
|
|||
/// using the control and store the results in <paramref name="dest"/>.
|
|||
/// </summary>
|
|||
/// <param name="source">The source span of bytes.</param>
|
|||
/// <param name="dest">The destination span of bytes.</param>
|
|||
/// <param name="shuffle">The type of shuffle to perform.</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void Shuffle3<TShuffle>( |
|||
ReadOnlySpan<byte> source, |
|||
Span<byte> dest, |
|||
TShuffle shuffle) |
|||
where TShuffle : struct, IShuffle3 |
|||
{ |
|||
VerifyShuffle3SpanInput(source, dest); |
|||
|
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
HwIntrinsics.Shuffle3Reduce(ref source, ref dest, shuffle.Control); |
|||
#endif
|
|||
|
|||
// Deal with the remainder:
|
|||
if (source.Length > 0) |
|||
{ |
|||
shuffle.RunFallbackShuffle(source, dest); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Pads then shuffles 8-bit integers within 128-bit lanes in <paramref name="source"/>
|
|||
/// using the control and store the results in <paramref name="dest"/>.
|
|||
/// </summary>
|
|||
/// <param name="source">The source span of bytes.</param>
|
|||
/// <param name="dest">The destination span of bytes.</param>
|
|||
/// <param name="shuffle">The type of shuffle to perform.</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void Pad3Shuffle4<TShuffle>( |
|||
ReadOnlySpan<byte> source, |
|||
Span<byte> dest, |
|||
TShuffle shuffle) |
|||
where TShuffle : struct, IPad3Shuffle4 |
|||
{ |
|||
VerifyPad3Shuffle4SpanInput(source, dest); |
|||
|
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
HwIntrinsics.Pad3Shuffle4Reduce(ref source, ref dest, shuffle.Control); |
|||
#endif
|
|||
|
|||
// Deal with the remainder:
|
|||
if (source.Length > 0) |
|||
{ |
|||
shuffle.RunFallbackShuffle(source, dest); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Shuffles then slices 8-bit integers within 128-bit lanes in <paramref name="source"/>
|
|||
/// using the control and store the results in <paramref name="dest"/>.
|
|||
/// </summary>
|
|||
/// <param name="source">The source span of bytes.</param>
|
|||
/// <param name="dest">The destination span of bytes.</param>
|
|||
/// <param name="shuffle">The type of shuffle to perform.</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void Shuffle4Slice3<TShuffle>( |
|||
ReadOnlySpan<byte> source, |
|||
Span<byte> dest, |
|||
TShuffle shuffle) |
|||
where TShuffle : struct, IShuffle4Slice3 |
|||
{ |
|||
VerifyShuffle4Slice3SpanInput(source, dest); |
|||
|
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
HwIntrinsics.Shuffle4Slice3Reduce(ref source, ref dest, shuffle.Control); |
|||
#endif
|
|||
|
|||
// Deal with the remainder:
|
|||
if (source.Length > 0) |
|||
{ |
|||
shuffle.RunFallbackShuffle(source, dest); |
|||
} |
|||
} |
|||
|
|||
private static void Shuffle4Remainder( |
|||
ReadOnlySpan<float> source, |
|||
Span<float> dest, |
|||
byte control) |
|||
{ |
|||
ref float sBase = ref MemoryMarshal.GetReference(source); |
|||
ref float dBase = ref MemoryMarshal.GetReference(dest); |
|||
Shuffle.InverseMmShuffle(control, out int p3, out int p2, out int p1, out int 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); |
|||
} |
|||
} |
|||
|
|||
[Conditional("DEBUG")] |
|||
private static void VerifyShuffle4SpanInput<T>(ReadOnlySpan<T> source, Span<T> dest) |
|||
where T : struct |
|||
{ |
|||
DebugGuard.IsTrue( |
|||
source.Length == dest.Length, |
|||
nameof(source), |
|||
"Input spans must be of same length!"); |
|||
|
|||
DebugGuard.IsTrue( |
|||
source.Length % 4 == 0, |
|||
nameof(source), |
|||
"Input spans must be divisable by 4!"); |
|||
} |
|||
|
|||
[Conditional("DEBUG")] |
|||
private static void VerifyShuffle3SpanInput<T>(ReadOnlySpan<T> source, Span<T> dest) |
|||
where T : struct |
|||
{ |
|||
DebugGuard.IsTrue( |
|||
source.Length == dest.Length, |
|||
nameof(source), |
|||
"Input spans must be of same length!"); |
|||
|
|||
DebugGuard.IsTrue( |
|||
source.Length % 3 == 0, |
|||
nameof(source), |
|||
"Input spans must be divisable by 3!"); |
|||
} |
|||
|
|||
[Conditional("DEBUG")] |
|||
private static void VerifyPad3Shuffle4SpanInput(ReadOnlySpan<byte> source, Span<byte> dest) |
|||
{ |
|||
DebugGuard.IsTrue( |
|||
source.Length % 3 == 0, |
|||
nameof(source), |
|||
"Input span must be divisable by 3!"); |
|||
|
|||
DebugGuard.IsTrue( |
|||
dest.Length % 4 == 0, |
|||
nameof(dest), |
|||
"Output span must be divisable by 4!"); |
|||
|
|||
DebugGuard.IsTrue( |
|||
source.Length == dest.Length * 3 / 4, |
|||
nameof(source), |
|||
"Input span must be 3/4 the length of the output span!"); |
|||
} |
|||
|
|||
[Conditional("DEBUG")] |
|||
private static void VerifyShuffle4Slice3SpanInput(ReadOnlySpan<byte> source, Span<byte> dest) |
|||
{ |
|||
DebugGuard.IsTrue( |
|||
source.Length % 4 == 0, |
|||
nameof(source), |
|||
"Input span must be divisable by 4!"); |
|||
|
|||
DebugGuard.IsTrue( |
|||
dest.Length % 3 == 0, |
|||
nameof(dest), |
|||
"Output span must be divisable by 3!"); |
|||
|
|||
DebugGuard.IsTrue( |
|||
dest.Length >= source.Length * 3 / 4, |
|||
nameof(source), |
|||
"Output span must be at least 3/4 the length of the input span!"); |
|||
} |
|||
|
|||
public static class Shuffle |
|||
{ |
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static byte MmShuffle(byte p3, byte p2, byte p1, byte p0) |
|||
=> (byte)((p3 << 6) | (p2 << 4) | (p1 << 2) | p0); |
|||
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void MmShuffleSpan(ref Span<byte> span, byte control) |
|||
{ |
|||
InverseMmShuffle( |
|||
control, |
|||
out int p3, |
|||
out int p2, |
|||
out int p1, |
|||
out int p0); |
|||
|
|||
ref byte spanBase = ref MemoryMarshal.GetReference(span); |
|||
|
|||
for (int i = 0; i < span.Length; i += 4) |
|||
{ |
|||
Unsafe.Add(ref spanBase, i) = (byte)(p0 + i); |
|||
Unsafe.Add(ref spanBase, i + 1) = (byte)(p1 + i); |
|||
Unsafe.Add(ref spanBase, i + 2) = (byte)(p2 + i); |
|||
Unsafe.Add(ref spanBase, i + 3) = (byte)(p3 + i); |
|||
} |
|||
} |
|||
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void InverseMmShuffle( |
|||
byte control, |
|||
out int p3, |
|||
out int p2, |
|||
out int p1, |
|||
out int p0) |
|||
{ |
|||
p3 = control >> 6 & 0x3; |
|||
p2 = control >> 4 & 0x3; |
|||
p1 = control >> 2 & 0x3; |
|||
p0 = control >> 0 & 0x3; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,122 +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; |
|||
|
|||
namespace SixLabors.ImageSharp |
|||
{ |
|||
/// <summary>
|
|||
/// Utility methods for the <see cref="Vector4"/> struct.
|
|||
/// </summary>
|
|||
internal static class Vector4Utilities |
|||
{ |
|||
/// <summary>
|
|||
/// Restricts a vector between a minimum and a maximum value.
|
|||
/// 5x Faster then <see cref="Vector4.Clamp(Vector4, Vector4, Vector4)"/>.
|
|||
/// </summary>
|
|||
/// <param name="x">The vector to restrict.</param>
|
|||
/// <param name="min">The minimum value.</param>
|
|||
/// <param name="max">The maximum value.</param>
|
|||
/// <returns>The <see cref="Vector4"/>.</returns>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static Vector4 FastClamp(Vector4 x, Vector4 min, Vector4 max) |
|||
=> Vector4.Min(Vector4.Max(x, min), max); |
|||
|
|||
/// <summary>
|
|||
/// Pre-multiplies the "x", "y", "z" components of a vector by its "w" component leaving the "w" component intact.
|
|||
/// </summary>
|
|||
/// <param name="source">The <see cref="Vector4"/> to premultiply</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void Premultiply(ref Vector4 source) |
|||
{ |
|||
float w = source.W; |
|||
source *= w; |
|||
source.W = w; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Reverses the result of premultiplying a vector via <see cref="Premultiply(ref Vector4)"/>.
|
|||
/// </summary>
|
|||
/// <param name="source">The <see cref="Vector4"/> to premultiply</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void UnPremultiply(ref Vector4 source) |
|||
{ |
|||
float w = source.W; |
|||
source /= w; |
|||
source.W = w; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Bulk variant of <see cref="Premultiply(ref Vector4)"/>
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void Premultiply(Span<Vector4> vectors) |
|||
{ |
|||
// TODO: This method can be AVX2 optimized using Vector<float>
|
|||
ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors); |
|||
|
|||
for (int i = 0; i < vectors.Length; i++) |
|||
{ |
|||
ref Vector4 v = ref Unsafe.Add(ref baseRef, i); |
|||
Premultiply(ref v); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Bulk variant of <see cref="UnPremultiply(ref Vector4)"/>
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void UnPremultiply(Span<Vector4> vectors) |
|||
{ |
|||
// TODO: This method can be AVX2 optimized using Vector<float>
|
|||
ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors); |
|||
|
|||
for (int i = 0; i < vectors.Length; i++) |
|||
{ |
|||
ref Vector4 v = ref Unsafe.Add(ref baseRef, i); |
|||
UnPremultiply(ref v); |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Transforms a vector by the given matrix.
|
|||
/// </summary>
|
|||
/// <param name="vector">The source vector.</param>
|
|||
/// <param name="matrix">The transformation matrix.</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Bulk variant of <see cref="Transform(ref Vector4, ref ColorMatrix)"/>.
|
|||
/// </summary>
|
|||
/// <param name="vectors">The span of vectors</param>
|
|||
/// <param name="matrix">The transformation matrix.</param>
|
|||
[MethodImpl(InliningOptions.ShortMethod)] |
|||
public static void Transform(Span<Vector4> 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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal abstract class Avx2JpegColorConverter : VectorizedJpegColorConverter |
|||
{ |
|||
protected Avx2JpegColorConverter(JpegColorSpace colorSpace, int precision) |
|||
: base(colorSpace, precision, 8) |
|||
{ |
|||
} |
|||
|
|||
protected sealed override bool IsAvailable => SimdUtils.HasAvx2; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal abstract class BasicJpegColorConverter : JpegColorConverter |
|||
{ |
|||
protected BasicJpegColorConverter(JpegColorSpace colorSpace, int precision) |
|||
: base(colorSpace, precision) |
|||
{ |
|||
} |
|||
|
|||
protected override bool IsAvailable => true; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,81 @@ |
|||
// 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; |
|||
using static SixLabors.ImageSharp.SimdUtils; |
|||
#endif
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal sealed class FromCmykAvx2 : Avx2JpegColorConverter |
|||
{ |
|||
public FromCmykAvx2(int precision) |
|||
: base(JpegColorSpace.Cmyk, precision) |
|||
{ |
|||
} |
|||
|
|||
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) |
|||
{ |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
ref Vector256<float> cBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector256<float> mBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector256<float> yBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|||
ref Vector256<float> kBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|||
|
|||
ref Vector256<float> resultBase = |
|||
ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(result)); |
|||
|
|||
// Used for the color conversion
|
|||
var scale = Vector256.Create(1 / this.MaximumValue); |
|||
var one = Vector256.Create(1F); |
|||
|
|||
// Used for packing
|
|||
ref byte control = ref MemoryMarshal.GetReference(HwIntrinsics.PermuteMaskEvenOdd8x32); |
|||
Vector256<int> vcontrol = Unsafe.As<byte, Vector256<int>>(ref control); |
|||
|
|||
int n = result.Length / 8; |
|||
for (int i = 0; i < n; i++) |
|||
{ |
|||
Vector256<float> k = Avx2.PermuteVar8x32(Unsafe.Add(ref kBase, i), vcontrol); |
|||
Vector256<float> c = Avx2.PermuteVar8x32(Unsafe.Add(ref cBase, i), vcontrol); |
|||
Vector256<float> m = Avx2.PermuteVar8x32(Unsafe.Add(ref mBase, i), vcontrol); |
|||
Vector256<float> y = Avx2.PermuteVar8x32(Unsafe.Add(ref yBase, i), vcontrol); |
|||
|
|||
k = Avx.Multiply(k, scale); |
|||
|
|||
c = Avx.Multiply(Avx.Multiply(c, k), scale); |
|||
m = Avx.Multiply(Avx.Multiply(m, k), scale); |
|||
y = Avx.Multiply(Avx.Multiply(y, k), scale); |
|||
|
|||
Vector256<float> cmLo = Avx.UnpackLow(c, m); |
|||
Vector256<float> yoLo = Avx.UnpackLow(y, one); |
|||
Vector256<float> cmHi = Avx.UnpackHigh(c, m); |
|||
Vector256<float> yoHi = Avx.UnpackHigh(y, one); |
|||
|
|||
ref Vector256<float> destination = ref Unsafe.Add(ref resultBase, i * 4); |
|||
|
|||
destination = Avx.Shuffle(cmLo, yoLo, 0b01_00_01_00); |
|||
Unsafe.Add(ref destination, 1) = Avx.Shuffle(cmLo, yoLo, 0b11_10_11_10); |
|||
Unsafe.Add(ref destination, 2) = Avx.Shuffle(cmHi, yoHi, 0b01_00_01_00); |
|||
Unsafe.Add(ref destination, 3) = Avx.Shuffle(cmHi, yoHi, 0b11_10_11_10); |
|||
} |
|||
#endif
|
|||
} |
|||
|
|||
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => |
|||
FromCmykBasic.ConvertCore(values, result, this.MaximumValue); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,71 @@ |
|||
// 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.Tuples; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal sealed class FromCmykVector8 : Vector8JpegColorConverter |
|||
{ |
|||
public FromCmykVector8(int precision) |
|||
: base(JpegColorSpace.Cmyk, precision) |
|||
{ |
|||
} |
|||
|
|||
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) |
|||
{ |
|||
ref Vector<float> cBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector<float> mBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector<float> yBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|||
ref Vector<float> kBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|||
|
|||
ref Vector4Octet resultBase = |
|||
ref Unsafe.As<Vector4, Vector4Octet>(ref MemoryMarshal.GetReference(result)); |
|||
|
|||
Vector4Pair cc = default; |
|||
Vector4Pair mm = default; |
|||
Vector4Pair yy = default; |
|||
ref Vector<float> ccRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref cc); |
|||
ref Vector<float> mmRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref mm); |
|||
ref Vector<float> yyRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref yy); |
|||
|
|||
var scale = new Vector<float>(1 / this.MaximumValue); |
|||
|
|||
// Walking 8 elements at one step:
|
|||
int n = result.Length / 8; |
|||
for (int i = 0; i < n; i++) |
|||
{ |
|||
Vector<float> c = Unsafe.Add(ref cBase, i); |
|||
Vector<float> m = Unsafe.Add(ref mBase, i); |
|||
Vector<float> y = Unsafe.Add(ref yBase, i); |
|||
Vector<float> k = Unsafe.Add(ref kBase, i) * scale; |
|||
|
|||
c = (c * k) * scale; |
|||
m = (m * k) * scale; |
|||
y = (y * k) * scale; |
|||
|
|||
ccRefAsVector = c; |
|||
mmRefAsVector = m; |
|||
yyRefAsVector = y; |
|||
|
|||
// Collect (c0,c1...c8) (m0,m1...m8) (y0,y1...y8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order:
|
|||
ref Vector4Octet destination = ref Unsafe.Add(ref resultBase, i); |
|||
destination.Pack(ref cc, ref mm, ref yy); |
|||
} |
|||
} |
|||
|
|||
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => |
|||
FromCmykBasic.ConvertCore(values, result, this.MaximumValue); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,63 @@ |
|||
// 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; |
|||
using static SixLabors.ImageSharp.SimdUtils; |
|||
#endif
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal sealed class FromGrayscaleAvx2 : Avx2JpegColorConverter |
|||
{ |
|||
public FromGrayscaleAvx2(int precision) |
|||
: base(JpegColorSpace.Grayscale, precision) |
|||
{ |
|||
} |
|||
|
|||
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) |
|||
{ |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
ref Vector256<float> gBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
|
|||
ref Vector256<float> resultBase = |
|||
ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(result)); |
|||
|
|||
// Used for the color conversion
|
|||
var scale = Vector256.Create(1 / this.MaximumValue); |
|||
var one = Vector256.Create(1F); |
|||
|
|||
// Used for packing
|
|||
ref byte control = ref MemoryMarshal.GetReference(HwIntrinsics.PermuteMaskEvenOdd8x32); |
|||
Vector256<int> vcontrol = Unsafe.As<byte, Vector256<int>>(ref control); |
|||
|
|||
int n = result.Length / 8; |
|||
for (int i = 0; i < n; i++) |
|||
{ |
|||
Vector256<float> g = Avx.Multiply(Unsafe.Add(ref gBase, i), scale); |
|||
|
|||
g = Avx2.PermuteVar8x32(g, vcontrol); |
|||
|
|||
ref Vector256<float> destination = ref Unsafe.Add(ref resultBase, i * 4); |
|||
|
|||
destination = Avx.Blend(Avx.Permute(g, 0b00_00_00_00), one, 0b1000_1000); |
|||
Unsafe.Add(ref destination, 1) = Avx.Blend(Avx.Shuffle(g, g, 0b01_01_01_01), one, 0b1000_1000); |
|||
Unsafe.Add(ref destination, 2) = Avx.Blend(Avx.Shuffle(g, g, 0b10_10_10_10), one, 0b1000_1000); |
|||
Unsafe.Add(ref destination, 3) = Avx.Blend(Avx.Shuffle(g, g, 0b11_11_11_11), one, 0b1000_1000); |
|||
} |
|||
#endif
|
|||
} |
|||
|
|||
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => |
|||
FromGrayscaleBasic.ConvertCore(values, result, this.MaximumValue); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,72 @@ |
|||
// 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; |
|||
using static SixLabors.ImageSharp.SimdUtils; |
|||
#endif
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal sealed class FromRgbAvx2 : Avx2JpegColorConverter |
|||
{ |
|||
public FromRgbAvx2(int precision) |
|||
: base(JpegColorSpace.RGB, precision) |
|||
{ |
|||
} |
|||
|
|||
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) |
|||
{ |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
ref Vector256<float> rBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector256<float> gBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector256<float> bBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|||
|
|||
ref Vector256<float> resultBase = |
|||
ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(result)); |
|||
|
|||
// Used for the color conversion
|
|||
var scale = Vector256.Create(1 / this.MaximumValue); |
|||
var one = Vector256.Create(1F); |
|||
|
|||
// Used for packing
|
|||
ref byte control = ref MemoryMarshal.GetReference(HwIntrinsics.PermuteMaskEvenOdd8x32); |
|||
Vector256<int> vcontrol = Unsafe.As<byte, Vector256<int>>(ref control); |
|||
|
|||
int n = result.Length / 8; |
|||
for (int i = 0; i < n; i++) |
|||
{ |
|||
Vector256<float> r = Avx.Multiply(Avx2.PermuteVar8x32(Unsafe.Add(ref rBase, i), vcontrol), scale); |
|||
Vector256<float> g = Avx.Multiply(Avx2.PermuteVar8x32(Unsafe.Add(ref gBase, i), vcontrol), scale); |
|||
Vector256<float> b = Avx.Multiply(Avx2.PermuteVar8x32(Unsafe.Add(ref bBase, i), vcontrol), scale); |
|||
|
|||
Vector256<float> rgLo = Avx.UnpackLow(r, g); |
|||
Vector256<float> boLo = Avx.UnpackLow(b, one); |
|||
Vector256<float> rgHi = Avx.UnpackHigh(r, g); |
|||
Vector256<float> boHi = Avx.UnpackHigh(b, one); |
|||
|
|||
ref Vector256<float> destination = ref Unsafe.Add(ref resultBase, i * 4); |
|||
|
|||
destination = Avx.Shuffle(rgLo, boLo, 0b01_00_01_00); |
|||
Unsafe.Add(ref destination, 1) = Avx.Shuffle(rgLo, boLo, 0b11_10_11_10); |
|||
Unsafe.Add(ref destination, 2) = Avx.Shuffle(rgHi, boHi, 0b01_00_01_00); |
|||
Unsafe.Add(ref destination, 3) = Avx.Shuffle(rgHi, boHi, 0b11_10_11_10); |
|||
} |
|||
#endif
|
|||
} |
|||
|
|||
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => |
|||
FromRgbBasic.ConvertCore(values, result, this.MaximumValue); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,67 @@ |
|||
// 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.Tuples; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal sealed class FromRgbVector8 : Vector8JpegColorConverter |
|||
{ |
|||
public FromRgbVector8(int precision) |
|||
: base(JpegColorSpace.RGB, precision) |
|||
{ |
|||
} |
|||
|
|||
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) |
|||
{ |
|||
ref Vector<float> rBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector<float> gBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector<float> bBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|||
|
|||
ref Vector4Octet resultBase = |
|||
ref Unsafe.As<Vector4, Vector4Octet>(ref MemoryMarshal.GetReference(result)); |
|||
|
|||
Vector4Pair rr = default; |
|||
Vector4Pair gg = default; |
|||
Vector4Pair bb = default; |
|||
ref Vector<float> rrRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref rr); |
|||
ref Vector<float> ggRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref gg); |
|||
ref Vector<float> bbRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref bb); |
|||
|
|||
var scale = new Vector<float>(1 / this.MaximumValue); |
|||
|
|||
// Walking 8 elements at one step:
|
|||
int n = result.Length / 8; |
|||
for (int i = 0; i < n; i++) |
|||
{ |
|||
Vector<float> r = Unsafe.Add(ref rBase, i); |
|||
Vector<float> g = Unsafe.Add(ref gBase, i); |
|||
Vector<float> b = Unsafe.Add(ref bBase, i); |
|||
r *= scale; |
|||
g *= scale; |
|||
b *= scale; |
|||
|
|||
rrRefAsVector = r; |
|||
ggRefAsVector = g; |
|||
bbRefAsVector = b; |
|||
|
|||
// Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order:
|
|||
ref Vector4Octet destination = ref Unsafe.Add(ref resultBase, i); |
|||
destination.Pack(ref rr, ref gg, ref bb); |
|||
} |
|||
} |
|||
|
|||
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => |
|||
FromRgbBasic.ConvertCore(values, result, this.MaximumValue); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,101 @@ |
|||
// 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; |
|||
using static SixLabors.ImageSharp.SimdUtils; |
|||
#endif
|
|||
|
|||
// ReSharper disable ImpureMethodCallOnReadonlyValueField
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal sealed class FromYCbCrAvx2 : Avx2JpegColorConverter |
|||
{ |
|||
public FromYCbCrAvx2(int precision) |
|||
: base(JpegColorSpace.YCbCr, precision) |
|||
{ |
|||
} |
|||
|
|||
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) |
|||
{ |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
ref Vector256<float> yBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector256<float> cbBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector256<float> crBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|||
|
|||
ref Vector256<float> resultBase = |
|||
ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(result)); |
|||
|
|||
// Used for the color conversion
|
|||
var chromaOffset = Vector256.Create(-this.HalfValue); |
|||
var scale = Vector256.Create(1 / this.MaximumValue); |
|||
var rCrMult = Vector256.Create(1.402F); |
|||
var gCbMult = Vector256.Create(-0.344136F); |
|||
var gCrMult = Vector256.Create(-0.714136F); |
|||
var bCbMult = Vector256.Create(1.772F); |
|||
|
|||
// Used for packing.
|
|||
var va = Vector256.Create(1F); |
|||
ref byte control = ref MemoryMarshal.GetReference(HwIntrinsics.PermuteMaskEvenOdd8x32); |
|||
Vector256<int> vcontrol = Unsafe.As<byte, Vector256<int>>(ref control); |
|||
|
|||
// Walking 8 elements at one step:
|
|||
int n = result.Length / 8; |
|||
for (int i = 0; i < n; i++) |
|||
{ |
|||
// y = yVals[i];
|
|||
// cb = cbVals[i] - 128F;
|
|||
// cr = crVals[i] - 128F;
|
|||
Vector256<float> y = Unsafe.Add(ref yBase, i); |
|||
Vector256<float> cb = Avx.Add(Unsafe.Add(ref cbBase, i), chromaOffset); |
|||
Vector256<float> cr = Avx.Add(Unsafe.Add(ref crBase, i), chromaOffset); |
|||
|
|||
y = Avx2.PermuteVar8x32(y, vcontrol); |
|||
cb = Avx2.PermuteVar8x32(cb, vcontrol); |
|||
cr = Avx2.PermuteVar8x32(cr, vcontrol); |
|||
|
|||
// r = y + (1.402F * cr);
|
|||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|||
// b = y + (1.772F * cb);
|
|||
// Adding & multiplying 8 elements at one time:
|
|||
Vector256<float> r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult); |
|||
Vector256<float> g = HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); |
|||
Vector256<float> b = HwIntrinsics.MultiplyAdd(y, cb, bCbMult); |
|||
|
|||
// TODO: We should be saving to RGBA not Vector4
|
|||
r = Avx.Multiply(Avx.RoundToNearestInteger(r), scale); |
|||
g = Avx.Multiply(Avx.RoundToNearestInteger(g), scale); |
|||
b = Avx.Multiply(Avx.RoundToNearestInteger(b), scale); |
|||
|
|||
Vector256<float> vte = Avx.UnpackLow(r, b); |
|||
Vector256<float> vto = Avx.UnpackLow(g, va); |
|||
|
|||
ref Vector256<float> destination = ref Unsafe.Add(ref resultBase, i * 4); |
|||
|
|||
destination = Avx.UnpackLow(vte, vto); |
|||
Unsafe.Add(ref destination, 1) = Avx.UnpackHigh(vte, vto); |
|||
|
|||
vte = Avx.UnpackHigh(r, b); |
|||
vto = Avx.UnpackHigh(g, va); |
|||
|
|||
Unsafe.Add(ref destination, 2) = Avx.UnpackLow(vte, vto); |
|||
Unsafe.Add(ref destination, 3) = Avx.UnpackHigh(vte, vto); |
|||
} |
|||
#endif
|
|||
} |
|||
|
|||
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => |
|||
FromYCbCrBasic.ConvertCore(values, result, this.MaximumValue, this.HalfValue); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,110 @@ |
|||
// 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; |
|||
using static SixLabors.ImageSharp.SimdUtils; |
|||
#endif
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal sealed class FromYccKAvx2 : Avx2JpegColorConverter |
|||
{ |
|||
public FromYccKAvx2(int precision) |
|||
: base(JpegColorSpace.Ycck, precision) |
|||
{ |
|||
} |
|||
|
|||
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) |
|||
{ |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
ref Vector256<float> yBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector256<float> cbBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector256<float> crBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|||
ref Vector256<float> kBase = |
|||
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|||
|
|||
ref Vector256<float> resultBase = |
|||
ref Unsafe.As<Vector4, Vector256<float>>(ref MemoryMarshal.GetReference(result)); |
|||
|
|||
// Used for the color conversion
|
|||
var chromaOffset = Vector256.Create(-this.HalfValue); |
|||
var scale = Vector256.Create(1 / this.MaximumValue); |
|||
var max = Vector256.Create(this.MaximumValue); |
|||
var rCrMult = Vector256.Create(1.402F); |
|||
var gCbMult = Vector256.Create(-0.344136F); |
|||
var gCrMult = Vector256.Create(-0.714136F); |
|||
var bCbMult = Vector256.Create(1.772F); |
|||
|
|||
// Used for packing.
|
|||
var va = Vector256.Create(1F); |
|||
ref byte control = ref MemoryMarshal.GetReference(HwIntrinsics.PermuteMaskEvenOdd8x32); |
|||
Vector256<int> vcontrol = Unsafe.As<byte, Vector256<int>>(ref control); |
|||
|
|||
// Walking 8 elements at one step:
|
|||
int n = result.Length / 8; |
|||
for (int i = 0; i < n; i++) |
|||
{ |
|||
// y = yVals[i];
|
|||
// cb = cbVals[i] - 128F;
|
|||
// cr = crVals[i] - 128F;
|
|||
// k = kVals[i] / 256F;
|
|||
Vector256<float> y = Unsafe.Add(ref yBase, i); |
|||
Vector256<float> cb = Avx.Add(Unsafe.Add(ref cbBase, i), chromaOffset); |
|||
Vector256<float> cr = Avx.Add(Unsafe.Add(ref crBase, i), chromaOffset); |
|||
Vector256<float> k = Avx.Divide(Unsafe.Add(ref kBase, i), max); |
|||
|
|||
y = Avx2.PermuteVar8x32(y, vcontrol); |
|||
cb = Avx2.PermuteVar8x32(cb, vcontrol); |
|||
cr = Avx2.PermuteVar8x32(cr, vcontrol); |
|||
k = Avx2.PermuteVar8x32(k, vcontrol); |
|||
|
|||
// r = y + (1.402F * cr);
|
|||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|||
// b = y + (1.772F * cb);
|
|||
// Adding & multiplying 8 elements at one time:
|
|||
Vector256<float> r = HwIntrinsics.MultiplyAdd(y, cr, rCrMult); |
|||
Vector256<float> g = |
|||
HwIntrinsics.MultiplyAdd(HwIntrinsics.MultiplyAdd(y, cb, gCbMult), cr, gCrMult); |
|||
Vector256<float> b = HwIntrinsics.MultiplyAdd(y, cb, bCbMult); |
|||
|
|||
r = Avx.Subtract(max, Avx.RoundToNearestInteger(r)); |
|||
g = Avx.Subtract(max, Avx.RoundToNearestInteger(g)); |
|||
b = Avx.Subtract(max, Avx.RoundToNearestInteger(b)); |
|||
|
|||
r = Avx.Multiply(Avx.Multiply(r, k), scale); |
|||
g = Avx.Multiply(Avx.Multiply(g, k), scale); |
|||
b = Avx.Multiply(Avx.Multiply(b, k), scale); |
|||
|
|||
Vector256<float> vte = Avx.UnpackLow(r, b); |
|||
Vector256<float> vto = Avx.UnpackLow(g, va); |
|||
|
|||
ref Vector256<float> destination = ref Unsafe.Add(ref resultBase, i * 4); |
|||
|
|||
destination = Avx.UnpackLow(vte, vto); |
|||
Unsafe.Add(ref destination, 1) = Avx.UnpackHigh(vte, vto); |
|||
|
|||
vte = Avx.UnpackHigh(r, b); |
|||
vto = Avx.UnpackHigh(g, va); |
|||
|
|||
Unsafe.Add(ref destination, 2) = Avx.UnpackLow(vte, vto); |
|||
Unsafe.Add(ref destination, 3) = Avx.UnpackHigh(vte, vto); |
|||
} |
|||
#endif
|
|||
} |
|||
|
|||
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => |
|||
FromYccKBasic.ConvertCore(values, result, this.MaximumValue, this.HalfValue); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,91 @@ |
|||
// 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.Tuples; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal sealed class FromYccKVector8 : Vector8JpegColorConverter |
|||
{ |
|||
public FromYccKVector8(int precision) |
|||
: base(JpegColorSpace.Ycck, precision) |
|||
{ |
|||
} |
|||
|
|||
protected override void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result) |
|||
{ |
|||
ref Vector<float> yBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component0)); |
|||
ref Vector<float> cbBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component1)); |
|||
ref Vector<float> crBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component2)); |
|||
ref Vector<float> kBase = |
|||
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(values.Component3)); |
|||
|
|||
ref Vector4Octet resultBase = |
|||
ref Unsafe.As<Vector4, Vector4Octet>(ref MemoryMarshal.GetReference(result)); |
|||
|
|||
var chromaOffset = new Vector<float>(-this.HalfValue); |
|||
|
|||
// Walking 8 elements at one step:
|
|||
int n = result.Length / 8; |
|||
|
|||
Vector4Pair rr = default; |
|||
Vector4Pair gg = default; |
|||
Vector4Pair bb = default; |
|||
|
|||
ref Vector<float> rrRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref rr); |
|||
ref Vector<float> ggRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref gg); |
|||
ref Vector<float> bbRefAsVector = ref Unsafe.As<Vector4Pair, Vector<float>>(ref bb); |
|||
|
|||
var scale = new Vector<float>(1 / this.MaximumValue); |
|||
var max = new Vector<float>(this.MaximumValue); |
|||
|
|||
for (int i = 0; i < n; i++) |
|||
{ |
|||
// y = yVals[i];
|
|||
// cb = cbVals[i] - 128F;
|
|||
// cr = crVals[i] - 128F;
|
|||
// k = kVals[i] / 256F;
|
|||
Vector<float> y = Unsafe.Add(ref yBase, i); |
|||
Vector<float> cb = Unsafe.Add(ref cbBase, i) + chromaOffset; |
|||
Vector<float> cr = Unsafe.Add(ref crBase, i) + chromaOffset; |
|||
Vector<float> k = Unsafe.Add(ref kBase, i) / max; |
|||
|
|||
// r = y + (1.402F * cr);
|
|||
// g = y - (0.344136F * cb) - (0.714136F * cr);
|
|||
// b = y + (1.772F * cb);
|
|||
// Adding & multiplying 8 elements at one time:
|
|||
Vector<float> r = y + (cr * new Vector<float>(1.402F)); |
|||
Vector<float> g = y - (cb * new Vector<float>(0.344136F)) - (cr * new Vector<float>(0.714136F)); |
|||
Vector<float> b = y + (cb * new Vector<float>(1.772F)); |
|||
|
|||
r = (max - r.FastRound()) * k; |
|||
g = (max - g.FastRound()) * k; |
|||
b = (max - b.FastRound()) * k; |
|||
r *= scale; |
|||
g *= scale; |
|||
b *= scale; |
|||
|
|||
rrRefAsVector = r; |
|||
ggRefAsVector = g; |
|||
bbRefAsVector = b; |
|||
|
|||
// Collect (r0,r1...r8) (g0,g1...g8) (b0,b1...b8) vector values in the expected (r0,g0,g1,1), (r1,g1,g2,1) ... order:
|
|||
ref Vector4Octet destination = ref Unsafe.Add(ref resultBase, i); |
|||
destination.Pack(ref rr, ref gg, ref bb); |
|||
} |
|||
} |
|||
|
|||
protected override void ConvertCore(in ComponentValues values, Span<Vector4> result) => |
|||
FromYccKBasic.ConvertCore(values, result, this.MaximumValue, this.HalfValue); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal abstract class Vector8JpegColorConverter : VectorizedJpegColorConverter |
|||
{ |
|||
protected Vector8JpegColorConverter(JpegColorSpace colorSpace, int precision) |
|||
: base(colorSpace, precision, 8) |
|||
{ |
|||
} |
|||
|
|||
protected sealed override bool IsAvailable => SimdUtils.HasVector8; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Numerics; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Decoder.ColorConverters |
|||
{ |
|||
internal abstract partial class JpegColorConverter |
|||
{ |
|||
internal abstract class VectorizedJpegColorConverter : JpegColorConverter |
|||
{ |
|||
private readonly int vectorSize; |
|||
|
|||
protected VectorizedJpegColorConverter(JpegColorSpace colorSpace, int precision, int vectorSize) |
|||
: base(colorSpace, precision) |
|||
{ |
|||
this.vectorSize = vectorSize; |
|||
} |
|||
|
|||
public sealed override void ConvertToRgba(in ComponentValues values, Span<Vector4> result) |
|||
{ |
|||
int remainder = result.Length % this.vectorSize; |
|||
int simdCount = result.Length - remainder; |
|||
if (simdCount > 0) |
|||
{ |
|||
// This implementation is actually AVX specific.
|
|||
// An AVX register is capable of storing 8 float-s.
|
|||
if (!this.IsAvailable) |
|||
{ |
|||
throw new InvalidOperationException( |
|||
"This converter can be used only on architecture having 256 byte floating point SIMD registers!"); |
|||
} |
|||
|
|||
this.ConvertCoreVectorized(values.Slice(0, simdCount), result.Slice(0, simdCount)); |
|||
} |
|||
|
|||
this.ConvertCore(values.Slice(simdCount, remainder), result.Slice(simdCount, remainder)); |
|||
} |
|||
|
|||
protected abstract void ConvertCoreVectorized(in ComponentValues values, Span<Vector4> result); |
|||
|
|||
protected abstract void ConvertCore(in ComponentValues values, Span<Vector4> result); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,120 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Diagnostics; |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
using System.Runtime.Intrinsics; |
|||
using System.Runtime.Intrinsics.X86; |
|||
#endif
|
|||
using SixLabors.ImageSharp.PixelFormats; |
|||
|
|||
namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder |
|||
{ |
|||
internal static class RgbToYCbCrConverterVectorized |
|||
{ |
|||
public static bool IsSupported |
|||
{ |
|||
get |
|||
{ |
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
return Avx2.IsSupported; |
|||
#else
|
|||
return false; |
|||
#endif
|
|||
} |
|||
} |
|||
|
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
private static ReadOnlySpan<byte> MoveFirst24BytesToSeparateLanes => new byte[] |
|||
{ |
|||
0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 6, 0, 0, 0, |
|||
3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0, 7, 0, 0, 0 |
|||
}; |
|||
|
|||
private static ReadOnlySpan<byte> MoveLast24BytesToSeparateLanes => new byte[] |
|||
{ |
|||
2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, |
|||
5, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, 0, 1, 0, 0, 0 |
|||
}; |
|||
|
|||
private static ReadOnlySpan<byte> ExtractRgb => new byte[] |
|||
{ |
|||
0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11, 0xFF, 0xFF, 0xFF, 0xFF, |
|||
0, 3, 6, 9, 1, 4, 7, 10, 2, 5, 8, 11, 0xFF, 0xFF, 0xFF, 0xFF |
|||
}; |
|||
#endif
|
|||
|
|||
public static void Convert(ReadOnlySpan<Rgb24> rgbSpan, ref Block8x8F yBlock, ref Block8x8F cbBlock, ref Block8x8F crBlock) |
|||
{ |
|||
Debug.Assert(IsSupported, "AVX2 is required to run this converter"); |
|||
|
|||
#if SUPPORTS_RUNTIME_INTRINSICS
|
|||
var f0299 = Vector256.Create(0.299f); |
|||
var f0587 = Vector256.Create(0.587f); |
|||
var f0114 = Vector256.Create(0.114f); |
|||
var fn0168736 = Vector256.Create(-0.168736f); |
|||
var fn0331264 = Vector256.Create(-0.331264f); |
|||
var f128 = Vector256.Create(128f); |
|||
var fn0418688 = Vector256.Create(-0.418688f); |
|||
var fn0081312F = Vector256.Create(-0.081312F); |
|||
var f05 = Vector256.Create(0.5f); |
|||
var zero = Vector256.Create(0).AsByte(); |
|||
|
|||
ref Vector256<byte> inRef = ref Unsafe.As<Rgb24, Vector256<byte>>(ref MemoryMarshal.GetReference(rgbSpan)); |
|||
ref Vector256<float> destYRef = ref Unsafe.As<Block8x8F, Vector256<float>>(ref yBlock); |
|||
ref Vector256<float> destCbRef = ref Unsafe.As<Block8x8F, Vector256<float>>(ref cbBlock); |
|||
ref Vector256<float> destCrRef = ref Unsafe.As<Block8x8F, Vector256<float>>(ref crBlock); |
|||
|
|||
var extractToLanesMask = Unsafe.As<byte, Vector256<uint>>(ref MemoryMarshal.GetReference(MoveFirst24BytesToSeparateLanes)); |
|||
var extractRgbMask = Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(ExtractRgb)); |
|||
Vector256<byte> rgb, rg, bx; |
|||
Vector256<float> r, g, b; |
|||
for (int i = 0; i < 7; i++) |
|||
{ |
|||
rgb = Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref inRef, (IntPtr)(24 * i)).AsUInt32(), extractToLanesMask).AsByte(); |
|||
|
|||
rgb = Avx2.Shuffle(rgb, extractRgbMask); |
|||
|
|||
rg = Avx2.UnpackLow(rgb, zero); |
|||
bx = Avx2.UnpackHigh(rgb, zero); |
|||
|
|||
r = Avx.ConvertToVector256Single(Avx2.UnpackLow(rg, zero).AsInt32()); |
|||
g = Avx.ConvertToVector256Single(Avx2.UnpackHigh(rg, zero).AsInt32()); |
|||
b = Avx.ConvertToVector256Single(Avx2.UnpackLow(bx, zero).AsInt32()); |
|||
|
|||
// (0.299F * r) + (0.587F * g) + (0.114F * b);
|
|||
Unsafe.Add(ref destYRef, i) = SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(f0114, b), f0587, g), f0299, r); |
|||
|
|||
// 128F + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b))
|
|||
Unsafe.Add(ref destCbRef, i) = Avx.Add(f128, SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(f05, b), fn0331264, g), fn0168736, r)); |
|||
|
|||
// 128F + ((0.5F * r) - (0.418688F * g) - (0.081312F * b))
|
|||
Unsafe.Add(ref destCrRef, i) = Avx.Add(f128, SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(fn0081312F, b), fn0418688, g), f05, r)); |
|||
} |
|||
|
|||
extractToLanesMask = Unsafe.As<byte, Vector256<uint>>(ref MemoryMarshal.GetReference(MoveLast24BytesToSeparateLanes)); |
|||
rgb = Avx2.PermuteVar8x32(Unsafe.AddByteOffset(ref inRef, (IntPtr)160).AsUInt32(), extractToLanesMask).AsByte(); |
|||
rgb = Avx2.Shuffle(rgb, extractRgbMask); |
|||
|
|||
rg = Avx2.UnpackLow(rgb, zero); |
|||
bx = Avx2.UnpackHigh(rgb, zero); |
|||
|
|||
r = Avx.ConvertToVector256Single(Avx2.UnpackLow(rg, zero).AsInt32()); |
|||
g = Avx.ConvertToVector256Single(Avx2.UnpackHigh(rg, zero).AsInt32()); |
|||
b = Avx.ConvertToVector256Single(Avx2.UnpackLow(bx, zero).AsInt32()); |
|||
|
|||
// (0.299F * r) + (0.587F * g) + (0.114F * b);
|
|||
Unsafe.Add(ref destYRef, 7) = SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(f0114, b), f0587, g), f0299, r); |
|||
|
|||
// 128F + ((-0.168736F * r) - (0.331264F * g) + (0.5F * b))
|
|||
Unsafe.Add(ref destCbRef, 7) = Avx.Add(f128, SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(f05, b), fn0331264, g), fn0168736, r)); |
|||
|
|||
// 128F + ((0.5F * r) - (0.418688F * g) - (0.081312F * b))
|
|||
Unsafe.Add(ref destCrRef, 7) = Avx.Add(f128, SimdUtils.HwIntrinsics.MultiplyAdd(SimdUtils.HwIntrinsics.MultiplyAdd(Avx.Multiply(fn0081312F, b), fn0418688, g), f05, r)); |
|||
#endif
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,60 @@ |
|||
// Copyright (c) Six Labors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
using System.Buffers; |
|||
|
|||
namespace SixLabors.ImageSharp.Memory |
|||
{ |
|||
/// <summary>
|
|||
/// A custom <see cref="MemoryManager{T}"/> that can wrap a rawpointer to a buffer of a specified type.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The value type to use when casting the wrapped <see cref="Memory{T}"/> instance.</typeparam>
|
|||
/// <remarks>This manager doesn't own the memory buffer that it points to.</remarks>
|
|||
internal sealed unsafe class UnmanagedMemoryManager<T> : MemoryManager<T> |
|||
where T : unmanaged |
|||
{ |
|||
/// <summary>
|
|||
/// The pointer to the memory buffer.
|
|||
/// </summary>
|
|||
private readonly void* pointer; |
|||
|
|||
/// <summary>
|
|||
/// The length of the memory area.
|
|||
/// </summary>
|
|||
private readonly int length; |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="UnmanagedMemoryManager{T}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="pointer">The pointer to the memory buffer.</param>
|
|||
/// <param name="length">The length of the memory area.</param>
|
|||
public UnmanagedMemoryManager(void* pointer, int length) |
|||
{ |
|||
this.pointer = pointer; |
|||
this.length = length; |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
protected override void Dispose(bool disposing) |
|||
{ |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override Span<T> GetSpan() |
|||
{ |
|||
return new Span<T>(this.pointer, this.length); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override MemoryHandle Pin(int elementIndex = 0) |
|||
{ |
|||
return new MemoryHandle(((T*)this.pointer) + elementIndex); |
|||
} |
|||
|
|||
/// <inheritdoc/>
|
|||
public override void Unpin() |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue