private long OneStatPass(int width, int height, int yStride, int uvStride, Vp8RdLevel rdOpt, int nbMbs, PassStats stats) { Span <byte> y = this.Y.GetSpan(); Span <byte> u = this.U.GetSpan(); Span <byte> v = this.V.GetSpan(); var it = new Vp8EncIterator(this.YTop, this.UvTop, this.Nz, this.MbInfo, this.Preds, this.TopDerr, this.Mbw, this.Mbh); long size = 0; long sizeP0 = 0; long distortion = 0; long pixelCount = nbMbs * 384; it.Init(); this.SetLoopParams(stats.Q); var info = new Vp8ModeScore(); do { info.Clear(); it.Import(y, u, v, yStride, uvStride, width, height, false); if (this.Decimate(it, ref info, rdOpt)) { // Just record the number of skips and act like skipProba is not used. ++this.Proba.NbSkip; } this.RecordResiduals(it, info); size += info.R + info.H; sizeP0 += info.H; distortion += info.D; it.SaveBoundary(); }while (it.Next() && --nbMbs > 0); sizeP0 += this.SegmentHeader.Size; if (stats.DoSizeSearch) { size += this.Proba.FinalizeSkipProba(this.Mbw, this.Mbh); size += this.Proba.FinalizeTokenProbas(); size = ((size + sizeP0 + 1024) >> 11) + HeaderSizeEstimate; stats.Value = size; } else { stats.Value = GetPsnr(distortion, pixelCount); } return(sizeP0); }
/// <summary> /// Encodes the image to the specified stream from the <see cref="Image{TPixel}"/>. /// </summary> /// <typeparam name="TPixel">The pixel format.</typeparam> /// <param name="image">The <see cref="Image{TPixel}"/> to encode from.</param> /// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param> public void Encode <TPixel>(Image <TPixel> image, Stream stream) where TPixel : unmanaged, IPixel <TPixel> { int width = image.Width; int height = image.Height; Span <byte> y = this.Y.GetSpan(); Span <byte> u = this.U.GetSpan(); Span <byte> v = this.V.GetSpan(); YuvConversion.ConvertRgbToYuv(image, this.configuration, this.memoryAllocator, y, u, v); int yStride = width; int uvStride = (yStride + 1) >> 1; var it = new Vp8EncIterator(this.YTop, this.UvTop, this.Nz, this.MbInfo, this.Preds, this.TopDerr, this.Mbw, this.Mbh); int[] alphas = new int[WebpConstants.MaxAlpha + 1]; this.alpha = this.MacroBlockAnalysis(width, height, it, y, u, v, yStride, uvStride, alphas, out this.uvAlpha); int totalMb = this.Mbw * this.Mbw; this.alpha /= totalMb; this.uvAlpha /= totalMb; // Analysis is done, proceed to actual encoding. this.SegmentHeader = new Vp8EncSegmentHeader(4); this.AssignSegments(alphas); this.SetLoopParams(this.quality); // Initialize the bitwriter. int averageBytesPerMacroBlock = this.averageBytesPerMb[this.BaseQuant >> 4]; int expectedSize = this.Mbw * this.Mbh * averageBytesPerMacroBlock; this.bitWriter = new Vp8BitWriter(expectedSize, this); // TODO: EncodeAlpha(); bool hasAlpha = false; // Stats-collection loop. this.StatLoop(width, height, yStride, uvStride); it.Init(); it.InitFilter(); var info = new Vp8ModeScore(); var residual = new Vp8Residual(); do { bool dontUseSkip = !this.Proba.UseSkipProba; info.Clear(); it.Import(y, u, v, yStride, uvStride, width, height, false); // Warning! order is important: first call VP8Decimate() and // *then* decide how to code the skip decision if there's one. if (!this.Decimate(it, ref info, this.rdOptLevel) || dontUseSkip) { this.CodeResiduals(it, info, residual); } else { it.ResetAfterSkip(); } it.SaveBoundary(); }while (it.Next()); // Store filter stats. this.AdjustFilterStrength(); // Write bytes from the bitwriter buffer to the stream. image.Metadata.SyncProfiles(); this.bitWriter.WriteEncodedImageToStream(stream, image.Metadata.ExifProfile, (uint)width, (uint)height, hasAlpha); }