-
-
Notifications
You must be signed in to change notification settings - Fork 851
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Promote PixelTypeInfo to Pixel #2601
Promote PixelTypeInfo to Pixel #2601
Conversation
|
||
internal static PixelTypeInfo Create<TPixel>(PixelAlphaRepresentation alpha) | ||
internal static PixelTypeInfo Create<TPixel>(byte componentCount, PixelAlphaRepresentation pixelAlphaRepresentation) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering whether we can remove this? I want to add the additional enum #2534 (comment) which means adding an additional property to the constructor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe just add the additional parameter as having to duplicate Unsafe.SizeOf<TPixel>() * 8
is worse.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stefannikolei I'm going to add this for you today.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah. To be honest.. totally forgot about that comment
@stefannikolei I've made my changes, will need to do a sense check in the morning before merging as it's late now. |
this.BitsPerPixel = bitsPerPixel; | ||
this.AlphaRepresentation = alpha; | ||
} | ||
public byte ComponentCount { get; init; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should consider changing this to int
.
public byte ComponentCount { get; init; } | |
public int ComponentCount { get; init; } |
Unless there is a reason to compress data for optimization (=the type is expected to be instantiated very frequently), it is generally recommended to prefer int
over other integral types in .NET APIs. For example, IPEndPoint.Port
is an int
, while technically it could be an ushort
. The reason is that this removes the need for conversion which is both expensive and complicated.
On the other hand, I noticed that we use non-int
integrals in other descriptor types, eg. PngMetadata
. I wish I noticed this earlier.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’ve often wondered if there was a rule. For v4 we can definitely fix this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately it's not codified in the Framework Design Guidelines, but it exists for BCL APIs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All good. I've fixed the property for this PR, we can look at others.
/// </summary> | ||
public int BitsPerPixel { get; } | ||
public PixelComponentPrecision? ComponentPrecision { get; init; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIK there might be pixel types with asymmetric component precision. (I remember briefly touching this topic in an old discussion with Clinton.)
How would this model handle such pixel types?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It represents the maximum component precision. It’s in the description but perhaps including it in the type name or property name is better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked at the usages of the property, and the only way we use it is the info.ComponentPrecision <= PixelComponentPrecision.Byte
comparison, meaning that the only thing we need this for is to determine if the precision is below or above a certain treshold, and the exact precision information is practically unused. Unless we think there are use cases (proven by actual user user need) to observe the precision of the highest precision component, IMO we should try to avoid exposing such a property and explore alternative options to solve this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My thought process is that users can implement custom performance optimized processors based upon this implementation. If they know the precision, they could operate over byte
vs Vector4
.
The use case that I've currently implemented fixes a bug we had also.
Rgba1010102
uses a uint
as its packed value which is the same size as Rgba32
but we lose precision for the RGB components if we convert to it as we were since they require 10 bit precision.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see that ComponentCount
is a similar informative property. I didn't notice this fact in the #2534 discussion. The way I view this now is that we are attempting to add a Rich Pixel Type Descriptor feature. IMO there are a couple of important points we should consider before fully committing to it:
- To really get this right, it might be better to expose a
struct/class ComponentInfo
and a property inPixelTypeInfo
that enumerates the ComponentInfos, so each component can tell its precision separately instead of telling the maximum (for which I'm not sure if there is a use-case except the conversion implementation this PR is proposing). As far as I can recall that old discussion with Clinton, WIC has such a model but I may be wrong. - Note that this feature idea doesn't necessarily overlap with the
Pixel <=> Color
conversion. I don't see how can we implement efficient bulk conversion using these descriptor information only. In fact, if our goal is to solve thePixel <=> Color
conversion thing, it might be better to promote the conversion (& bulk conversion) methods toPixelOperations<T>
. - Considering that this isn't super trivial to get right, I'm really curious if we have enough evidence (=enough users) proving that the Rich Pixel Type Descriptor feature is worth the headache.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's neat! I think you might have to be able to account for padding bits, so I'm not sure you can get away with not having a mask per channel. I'm trying to think of tricky examples... I've seen some BMPs that have gaps in the channel masks, but those were of the stress-test variety, not real world. You'll have real-world formats like B10G10R10, which is 32-bit with 2 bits padding, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure that's required for our use cases which is what precision can I safly store this pixel in.
We can tell the actual size of the pixel format from the info since the types themselves must handle padding. Unsafe.Sizeof<B10G10R10>()
should be the same size Unsafe.Sizeof<Rgba1010102>()
which is 4
.
Theoretically we could add padding to the info. I'm assuming though that world is not completely mad and padding is always at the end?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, I don't think you'll see a real-world case where BitsPerPixel - Sum(ChannelPrecision)
doesn't give you the padding bits, which should always be at the end. Just trying to come up with reasons explicit masks might be beneficial, and weirdo pixel formats with interior padding are at least a possibility.
The other thing I guess that's useful in an explicit channel mask is that it also communicates the channel order. I'm not sure if WIC actually uses that information in that way, but I suppose it might. They don't expose channel order in any other way in their format definitions. It's just in the name and in the mask.
Edit: Never mind, there's no channel order conveyed in the mask. BGR and RGB have the same masks, so you can only know how to interpret them by knowing the name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'll go with real world for now. If someone wants something really weird, they can find a really weird library.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type has been introduced as PixelComponentInfo
@@ -124,7 +125,9 @@ public static Color FromPixel<TPixel>(TPixel pixel) | |||
{ | |||
return new((L16)(object)pixel); | |||
} | |||
else if (Unsafe.SizeOf<TPixel>() <= Unsafe.SizeOf<Rgba32>()) | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While not critical, I would prefer an abstraction that can get rid of the Rgba64-L16
special cases above this line - by moving them to IPixel<T>
/ PixelOperations
code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Me also. In this case I think we should ditch the specialisation and box anything greater than byte. ColorFromPixel
is not area for high performance operations.
On IPixel
I would, if we can, drop all the various FromXX
specialisation there also and ensure a good optimised bulk operations for all Rgba32
compatible pixel formats. That may not be possible though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ColorFromPixel
is not area for high performance operations.
Color.ToPixel
already has a bulk variant, and I remember that we have found a use case for the inverse bulk operation: #2485 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well remembered, but those are really short loops (max 256) in real terms. I'd hate to limit our flexibility and extensibility based on introducing a requirement there.
Ideally, I want to introduce as much agnostity as possible in our APIs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Boxing is very ugly, even if done <=256x
typically, I would try to avoid it if there are alternatives.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we just back Color with Vector4
? it already takes up the same amount of space and means we would be able to use bulk operations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason we introduced the ability to define (higher than Vector4
) arbitrary-precison pixel types was this feature request: SixLabors/ImageSharp.Drawing#165
I like the fact that ImageSharp supports such "images". (At least for some use-cases)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then use Vector4
as the default and box anything bigger. People expected double precision are in for a rough time with the library since almost everything is converted to use single precision at some point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've updated Color
to use Vector4
to avoid boxing. I've also removed the implicit casting for random pixel formats in favour of explicit methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm happy with this now. Would love a second or third opinion though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really nice improvement!
Fixes #2534