Pixel Formats
Pixel formats are one of the places where ImageSharp differs most clearly from older imaging APIs. The pixel type is not an afterthought or a hidden enum value; it is part of the image's actual type, which makes low-level code more explicit and more predictable once the model clicks.
Image<TPixel> is generic because the in-memory pixel type is part of the image contract. An Image<Rgba32> and an Image<L8> can represent the same picture, but they differ in channel layout, precision, memory usage, and what direct pixel access means for your code.
Image memory is usually treated as discontiguous, even though smaller images may fit in a single backing buffer. See Memory Management for more detail on how ImageSharp stores large images efficiently.
For multi-frame images, the individual bitmaps live in image.Frames as ImageFrame<TPixel> instances.
What Counts as a Pixel Format
A pixel format in ImageSharp is not just any color-related struct. To be used as TPixel, a type must implement IPixel<TSelf>.
That contract includes conversion members such as:
ToRgba32()ToScaledVector4()ToVector4()FromScaledVector4(...)FromVector4(...)- conversions to and from canonical pixel types such as
Rgba32,Rgb24,Bgra32,L8, andRgba64
This is what keeps the image processing pipeline practical. Many operations and batched conversion paths assume pixels can move efficiently through RGBA-oriented Vector4 representations, and some optimized paths are specifically designed for Rgba32-compatible pixel types where ToVector4() and FromVector4(...) are not expensive.
Pixel Formats Are Not Color Profile Types
This is separate from the color-profile conversion APIs described in Color Profiles and Color Conversion.
Types such as Rgb, Cmyk, Hsl, YCbCr, CieLab, and CieXyz are color value types used by ColorProfileConverter. They are not TPixel implementations for Image<TPixel>.
That means ImageSharp can convert color values between spaces like RGB, CMYK, Lab, and XYZ without treating those color models as general-purpose in-memory image storage formats. ImageSharp pixel formats are intentionally limited to types that fit the RGBA-oriented processing pipeline without expensive per-pixel translation on every operation.
Choosing Pixel Formats
Choose a TPixel based on the kind of in-memory work you need to do:
- Use
Rgba32as the general-purpose default. - Use lower-memory formats such as
Rgb24orL8when you know you do not need the extra channels or precision. - Use higher-precision formats such as
Rgb48,Rgba64, orRgbaVectorwhen your pipeline benefits from more precision.
For application code, a good default rule is to decode into the format you plan to process in, not necessarily the format the file used on disk. A JPEG may decode naturally into RGB-like data, but Image<Rgba32> can still be the right working type if the next step composites with alpha, draws overlays, or passes pixels to an API that expects RGBA. Conversely, Image<L8> can be the right type for masks, analysis, and grayscale-only processing where carrying color channels would just waste memory.
Do not use a higher-precision format only because the source file is high quality. Use it when the operations you run benefit from the extra range or precision, such as repeated color transforms, scientific-style image data, high-bit-depth exports, or workflows where banding from 8-bit intermediate values would be visible.
If you want to inspect pixel characteristics before a full decode, ImageInfo.PixelType exposes PixelTypeInfo. See Read Image Info Without Decoding for more on that workflow.
Defining Custom Pixel Formats
You can define a custom pixel format by creating a struct that implements IPixel<TSelf> and using it as the generic argument for Image<TPixel>.
Baseline batched conversion primitives are provided by PixelOperations<TPixel>, and you can override those implementations if you have a more efficient specialization.
In practice, custom TPixel types should still fit the same RGBA-compatible conversion model as the built-in formats. Many of the packed and vector-style pixel types are deliberately in the same family as graphics-oriented packed color representations, and IPackedVector<TPacked> follows the same packed-value shape used by MonoGame and XNA types, which allows signature compatibility with them.
Single-Bit Monochrome Pixels
ImageSharp does not currently support sub-byte TPixel formats such as a true 1-bit pixel type. That trade-off keeps the processing model and API surface much simpler, and it avoids paying a heavy CPU cost across the rest of the pipeline for a niche storage optimization.
Choosing a Working Pixel Format
Use Image<Rgba32> as the default when you need predictable direct pixel access and no special memory or precision constraint pushes you elsewhere. It is a practical working format for composition, overlays, custom row processing, and interop with APIs that expect RGBA-like data.
Choose lower-memory formats only when the missing channels or precision are genuinely unnecessary. L8 is a good fit for masks and grayscale analysis; Rgb24 can be useful when alpha is not part of the workflow. Choose higher-precision formats because the processing pipeline benefits from them, such as repeated color transforms or high-bit-depth output, not simply because the source file is "high quality".
Pixel format and color profile are related but separate decisions. Image<Rgba32> tells you the in-memory channel layout and numeric representation; ICC and CICP handling tell you how color values should be interpreted or converted. A robust pipeline chooses both deliberately.