-
-
Notifications
You must be signed in to change notification settings - Fork 887
Grayscale Jpeg encoding #1586
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
Grayscale Jpeg encoding #1586
Changes from 1 commit
7eaae92
6cff023
6dbde61
26d6112
096ef3d
d844e70
4d0fb40
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| // Copyright (c) Six Labors. | ||
| // Licensed under the Apache License, Version 2.0. | ||
|
|
||
| using System; | ||
| using System.Runtime.CompilerServices; | ||
| using SixLabors.ImageSharp.PixelFormats; | ||
|
|
||
| namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder | ||
| { | ||
| /// <summary> | ||
| /// Provides 8-bit lookup tables for converting from L8 to Y colorspace. | ||
| /// </summary> | ||
| internal unsafe struct L8ToYConverter | ||
| { | ||
| /// <summary> | ||
| /// Initializes | ||
| /// </summary> | ||
| /// <returns>The initialized <see cref="L8ToYConverter"/></returns> | ||
| public static L8ToYConverter Create() | ||
| { | ||
| L8ToYConverter converter = default; | ||
| return converter; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Optimized method to allocates the correct y, cb, and cr values to the DCT blocks from the given r, g, b values. | ||
| /// </summary> | ||
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
| private void ConvertPixelInto( | ||
| int l, | ||
| ref Block8x8F yResult, | ||
| int i) => yResult[i] = l; | ||
|
|
||
| public void Convert(Span<L8> l8Span, ref Block8x8F yBlock) | ||
| { | ||
| ref L8 l8Start = ref l8Span[0]; | ||
|
|
||
| for (int i = 0; i < 64; i++) | ||
| { | ||
| ref L8 c = ref Unsafe.Add(ref l8Start, i); | ||
|
|
||
| this.ConvertPixelInto( | ||
| c.PackedValue, | ||
| ref yBlock, | ||
| i); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| // Copyright (c) Six Labors. | ||
| // Licensed under the Apache License, Version 2.0. | ||
|
|
||
| using System; | ||
| using SixLabors.ImageSharp.Advanced; | ||
| using SixLabors.ImageSharp.PixelFormats; | ||
|
|
||
| namespace SixLabors.ImageSharp.Formats.Jpeg.Components.Encoder | ||
| { | ||
| /// <summary> | ||
| /// On-stack worker struct to efficiently encapsulate the TPixel -> L8 -> Y conversion chain of 8x8 pixel blocks. | ||
| /// </summary> | ||
| /// <typeparam name="TPixel">The pixel type to work on</typeparam> | ||
| internal ref struct LuminanceForwardConverter<TPixel> | ||
| where TPixel : unmanaged, IPixel<TPixel> | ||
| { | ||
| /// <summary> | ||
| /// The Y component | ||
| /// </summary> | ||
| public Block8x8F Y; | ||
|
|
||
| /// <summary> | ||
| /// The converter | ||
| /// </summary> | ||
| private L8ToYConverter converter; | ||
|
|
||
| /// <summary> | ||
| /// Temporal 8x8 block to hold TPixel data | ||
| /// </summary> | ||
| private GenericBlock8x8<TPixel> pixelBlock; | ||
|
|
||
| /// <summary> | ||
| /// Temporal RGB block | ||
| /// </summary> | ||
| private GenericBlock8x8<L8> l8Block; | ||
|
|
||
| public static LuminanceForwardConverter<TPixel> Create() | ||
| { | ||
| var result = default(LuminanceForwardConverter<TPixel>); | ||
| result.converter = L8ToYConverter.Create(); | ||
| return result; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Converts a 8x8 image area inside 'pixels' at position (x,y) placing the result members of the structure (<see cref="Y"/>) | ||
| /// </summary> | ||
| public void Convert(ImageFrame<TPixel> frame, int x, int y, ref RowOctet<TPixel> currentRows) | ||
| { | ||
| this.pixelBlock.LoadAndStretchEdges(frame.PixelBuffer, x, y, ref currentRows); | ||
|
|
||
| Span<L8> l8Span = this.l8Block.AsSpanUnsafe(); | ||
| PixelOperations<TPixel>.Instance.ToL8(frame.GetConfiguration(), this.pixelBlock.AsSpanUnsafe(), l8Span); | ||
|
|
||
| ref Block8x8F yBlock = ref this.Y; | ||
|
|
||
| this.converter.Convert(l8Span, ref yBlock); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| // Copyright (c) Six Labors. | ||
| // Licensed under the Apache License, Version 2.0. | ||
|
|
||
| namespace SixLabors.ImageSharp.Formats.Jpeg | ||
| { | ||
| /// <summary> | ||
| /// Provides enumeration of available JPEG color types. | ||
| /// </summary> | ||
| public enum JpegColorType : byte | ||
| { | ||
| /// <summary> | ||
| /// YCbCr (luminance, blue chroma, red chroma) color as defined in the ITU-T T.871 specification. | ||
| /// </summary> | ||
| YCbCr = 0, | ||
|
|
||
| /// <summary> | ||
| /// Single channel, luminance. | ||
| /// </summary> | ||
| Luminance = 1 | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,6 +25,11 @@ public sealed class JpegEncoder : IImageEncoder, IJpegEncoderOptions | |
| /// </summary> | ||
| public JpegSubsample? Subsample { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the color type, that will be used to encode the image. | ||
| /// </summary> | ||
| public JpegColorType? ColorType { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>. | ||
| /// </summary> | ||
|
|
@@ -35,6 +40,7 @@ public void Encode<TPixel>(Image<TPixel> image, Stream stream) | |
| where TPixel : unmanaged, IPixel<TPixel> | ||
| { | ||
| var encoder = new JpegEncoderCore(this); | ||
| this.EnrichColorType<TPixel>(); | ||
| encoder.Encode(image, stream); | ||
| } | ||
|
|
||
|
|
@@ -50,7 +56,21 @@ public Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, Cancellation | |
| where TPixel : unmanaged, IPixel<TPixel> | ||
| { | ||
| var encoder = new JpegEncoderCore(this); | ||
| this.EnrichColorType<TPixel>(); | ||
| return encoder.EncodeAsync(image, stream, cancellationToken); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// If ColorType was not set, set it based on the given <typeparamref name="TPixel"/>. | ||
| /// </summary> | ||
| private void EnrichColorType<TPixel>() | ||
ynse01 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| where TPixel : unmanaged, IPixel<TPixel> | ||
| { | ||
| if (this.ColorType == null) | ||
| { | ||
| bool isGrayscale = typeof(TPixel) == typeof(L8) || typeof(TPixel) == typeof(L16); | ||
|
||
| this.ColorType = isGrayscale ? JpegColorType.Luminance : JpegColorType.YCbCr; | ||
| } | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.