1212#include < AK/Function.h>
1313#include < LibGfx/Bitmap.h>
1414#include < LibGfx/CMYKBitmap.h>
15+ #include < LibGfx/Vector2.h>
1516
1617namespace Gfx {
1718
@@ -98,6 +99,40 @@ class JPEGBigEndianOutputBitStream : public Stream {
9899 size_t m_bit_offset { 0 };
99100};
100101
102+ void interpolate (f32 * component, f32 max_value, i8 start, i8 stop)
103+ {
104+ // We're creating a uniform (ɑ = 1) Catmull–Rom curve for the missing points.
105+ // That means that tᵢ₊₁ = tᵢ + 1.
106+ // Note that component[start] should be interpolated but component[stop] should not.
107+
108+ // p1 and p2 are set to the ceil value.
109+ // p0 is set to the last non-max value if possible, otherwise the value of p3 for symmetry.
110+ // The same logic is applied to p3.
111+ f32 const p0 = start == 0 ? component[zigzag_map[stop]] : component[zigzag_map[start - 1 ]];
112+ f32 const p1 = max_value;
113+ f32 const p2 = max_value;
114+ f32 const p3 = stop > 63 ? p0 : component[zigzag_map[stop]];
115+
116+ f32 const t0 = 0 .0f ;
117+ f32 const t1 = 1 ;
118+ f32 const t2 = 2 ;
119+ f32 const t3 = 3 ;
120+
121+ f32 const step = 1 . / (stop - start + 1 );
122+ f32 t = t1;
123+ for (i8 i = start; i < stop; ++i) {
124+ t += step;
125+ f32 const A1 = p0 * (t1 - t) / (t1 - t0) + p1 * (t - t0) / (t1 - t0);
126+ f32 const A2 = p1 * (t2 - t) / (t2 - t1) + p2 * (t - t1) / (t2 - t1);
127+ f32 const A3 = p2 * (t3 - t) / (t3 - t2) + p3 * (t - t2) / (t3 - t2);
128+ f32 const B1 = A1 * (t2 - t) / (t2 - t0) + A2 * (t - t0) / (t2 - t0);
129+ f32 const B2 = A2 * (t3 - t) / (t3 - t1) + A3 * (t - t1) / (t3 - t1);
130+ f32 const C = B1 * (t2 - t) / (t2 - t1) + B2 * (t - t1) / (t2 - t1);
131+
132+ component[zigzag_map[i]] = C;
133+ }
134+ }
135+
101136class JPEGEncodingContext {
102137public:
103138 JPEGEncodingContext (JPEGBigEndianOutputBitStream output_stream)
@@ -256,6 +291,35 @@ class JPEGEncodingContext {
256291 }
257292 }
258293
294+ void apply_deringing ()
295+ {
296+ // The method used here is described at: https://kornel.ski/deringing/.
297+
298+ for (auto & macroblock : m_macroblocks) {
299+ for (auto component : { macroblock.r , macroblock.g , macroblock.b }) {
300+ static constexpr auto maximum_value = NumericLimits<u8 >::max ();
301+ Optional<u8 > start;
302+ u8 i = 0 ;
303+ for (; i < 64 ; ++i) {
304+ if (component[zigzag_map[i]] == maximum_value) {
305+ if (!start.has_value ())
306+ start = i;
307+ else
308+ continue ;
309+ } else {
310+ if (start.has_value () && i - *start > 2 ) {
311+ interpolate (component, maximum_value, *start, i);
312+ }
313+ start.clear ();
314+ }
315+ }
316+
317+ if (start != 0 && component[zigzag_map[63 ]] == maximum_value)
318+ interpolate (component, maximum_value, *start, 64 );
319+ }
320+ }
321+ }
322+
259323 ErrorOr<void > write_huffman_stream (Mode mode)
260324 {
261325 for (auto & float_macroblock : m_macroblocks) {
@@ -633,8 +697,10 @@ ErrorOr<void> add_headers(Stream& stream, JPEGEncodingContext& context, JPEGWrit
633697 return {};
634698}
635699
636- ErrorOr<void > add_image (Stream& stream, JPEGEncodingContext& context, Mode mode)
700+ ErrorOr<void > add_image (Stream& stream, JPEGEncodingContext& context, JPEGWriter::Options const & options, Mode mode)
637701{
702+ if (options.use_deringing == JPEGEncoderOptions::UseDeringing::Yes)
703+ context.apply_deringing ();
638704 context.convert_to_ycbcr (mode);
639705 context.fdct_and_quantization (mode);
640706 TRY (context.write_huffman_stream (mode));
@@ -649,7 +715,7 @@ ErrorOr<void> JPEGWriter::encode(Stream& stream, Bitmap const& bitmap, Options c
649715 JPEGEncodingContext context { JPEGBigEndianOutputBitStream { stream } };
650716 TRY (add_headers (stream, context, options, bitmap.size (), Mode::RGB));
651717 TRY (context.initialize_mcu (bitmap));
652- TRY (add_image (stream, context, Mode::RGB));
718+ TRY (add_image (stream, context, options, Mode::RGB));
653719 return {};
654720}
655721
@@ -658,7 +724,7 @@ ErrorOr<void> JPEGWriter::encode(Stream& stream, CMYKBitmap const& bitmap, Optio
658724 JPEGEncodingContext context { JPEGBigEndianOutputBitStream { stream } };
659725 TRY (add_headers (stream, context, options, bitmap.size (), Mode::CMYK));
660726 TRY (context.initialize_mcu (bitmap));
661- TRY (add_image (stream, context, Mode::CMYK));
727+ TRY (add_image (stream, context, options, Mode::CMYK));
662728 return {};
663729}
664730
0 commit comments