@@ -98,6 +98,40 @@ class JPEGBigEndianOutputBitStream : public Stream {
9898 size_t m_bit_offset { 0 };
9999};
100100
101+ void interpolate (f32 * component, f32 max_value, i8 start, i8 stop)
102+ {
103+ // We're creating a uniform (ɑ = 1) Catmull–Rom curve for the missing points.
104+ // That means that tᵢ₊₁ = tᵢ + 1.
105+ // Note that component[start] should be interpolated but component[stop] should not.
106+
107+ // p1 and p2 are set to the ceil value.
108+ // p0 is set to the last non-max value if possible, otherwise the value of p3 for symmetry.
109+ // The same logic is applied to p3.
110+ f32 const p0 = start == 0 ? component[zigzag_map[stop]] : component[zigzag_map[start - 1 ]];
111+ f32 const p1 = max_value;
112+ f32 const p2 = max_value;
113+ f32 const p3 = stop > 63 ? p0 : component[zigzag_map[stop]];
114+
115+ f32 const t0 = 0 .0f ;
116+ f32 const t1 = 1 ;
117+ f32 const t2 = 2 ;
118+ f32 const t3 = 3 ;
119+
120+ f32 const step = 1 . / (stop - start + 1 );
121+ f32 t = t1;
122+ for (i8 i = start; i < stop; ++i) {
123+ t += step;
124+ f32 const A1 = p0 * (t1 - t) / (t1 - t0) + p1 * (t - t0) / (t1 - t0);
125+ f32 const A2 = p1 * (t2 - t) / (t2 - t1) + p2 * (t - t1) / (t2 - t1);
126+ f32 const A3 = p2 * (t3 - t) / (t3 - t2) + p3 * (t - t2) / (t3 - t2);
127+ f32 const B1 = A1 * (t2 - t) / (t2 - t0) + A2 * (t - t0) / (t2 - t0);
128+ f32 const B2 = A2 * (t3 - t) / (t3 - t1) + A3 * (t - t1) / (t3 - t1);
129+ f32 const C = B1 * (t2 - t) / (t2 - t1) + B2 * (t - t1) / (t2 - t1);
130+
131+ component[zigzag_map[i]] = C;
132+ }
133+ }
134+
101135class JPEGEncodingContext {
102136public:
103137 JPEGEncodingContext (JPEGBigEndianOutputBitStream output_stream)
@@ -256,6 +290,35 @@ class JPEGEncodingContext {
256290 }
257291 }
258292
293+ void apply_deringing ()
294+ {
295+ // The method used here is described at: https://kornel.ski/deringing/.
296+
297+ for (auto & macroblock : m_macroblocks) {
298+ for (auto component : { macroblock.r , macroblock.g , macroblock.b }) {
299+ static constexpr auto maximum_value = NumericLimits<u8 >::max ();
300+ Optional<u8 > start;
301+ u8 i = 0 ;
302+ for (; i < 64 ; ++i) {
303+ if (component[zigzag_map[i]] == maximum_value) {
304+ if (!start.has_value ())
305+ start = i;
306+ else
307+ continue ;
308+ } else {
309+ if (start.has_value () && i - *start > 2 ) {
310+ interpolate (component, maximum_value, *start, i);
311+ }
312+ start.clear ();
313+ }
314+ }
315+
316+ if (start != 0 && component[zigzag_map[63 ]] == maximum_value)
317+ interpolate (component, maximum_value, *start, 64 );
318+ }
319+ }
320+ }
321+
259322 ErrorOr<void > write_huffman_stream (Mode mode)
260323 {
261324 for (auto & float_macroblock : m_macroblocks) {
@@ -633,8 +696,10 @@ ErrorOr<void> add_headers(Stream& stream, JPEGEncodingContext& context, JPEGWrit
633696 return {};
634697}
635698
636- ErrorOr<void > add_image (Stream& stream, JPEGEncodingContext& context, Mode mode)
699+ ErrorOr<void > add_image (Stream& stream, JPEGEncodingContext& context, JPEGWriter::Options const & options, Mode mode)
637700{
701+ if (options.use_deringing == JPEGEncoderOptions::UseDeringing::Yes)
702+ context.apply_deringing ();
638703 context.convert_to_ycbcr (mode);
639704 context.fdct_and_quantization (mode);
640705 TRY (context.write_huffman_stream (mode));
@@ -649,7 +714,7 @@ ErrorOr<void> JPEGWriter::encode(Stream& stream, Bitmap const& bitmap, Options c
649714 JPEGEncodingContext context { JPEGBigEndianOutputBitStream { stream } };
650715 TRY (add_headers (stream, context, options, bitmap.size (), Mode::RGB));
651716 TRY (context.initialize_mcu (bitmap));
652- TRY (add_image (stream, context, Mode::RGB));
717+ TRY (add_image (stream, context, options, Mode::RGB));
653718 return {};
654719}
655720
@@ -658,7 +723,7 @@ ErrorOr<void> JPEGWriter::encode(Stream& stream, CMYKBitmap const& bitmap, Optio
658723 JPEGEncodingContext context { JPEGBigEndianOutputBitStream { stream } };
659724 TRY (add_headers (stream, context, options, bitmap.size (), Mode::CMYK));
660725 TRY (context.initialize_mcu (bitmap));
661- TRY (add_image (stream, context, Mode::CMYK));
726+ TRY (add_image (stream, context, options, Mode::CMYK));
662727 return {};
663728}
664729
0 commit comments