commit 9aa251ee1d7611cd27e3be0124f243f713384b5c Author: Gericom Date: Thu Oct 13 11:27:48 2022 +0200 Initial commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f790ab8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,365 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +!FastVideoDSEncoder/x64/ \ No newline at end of file diff --git a/FastVideoDS.sln b/FastVideoDS.sln new file mode 100644 index 0000000..5b28a9b --- /dev/null +++ b/FastVideoDS.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.2.32505.173 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Gericom.FastVideoDS", "Gericom.FastVideoDS\Gericom.FastVideoDS.csproj", "{AC320DAA-A040-4654-A4F2-238224723834}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastVideoDSEncoder", "FastVideoDSEncoder\FastVideoDSEncoder.csproj", "{6D06AE63-9EB8-4FFA-891C-455CB95A23BD}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FastVideoDSInfo", "FastVideoDSInfo\FastVideoDSInfo.csproj", "{BC08ACC4-8B61-4801-8532-D5D054795809}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AC320DAA-A040-4654-A4F2-238224723834}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AC320DAA-A040-4654-A4F2-238224723834}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AC320DAA-A040-4654-A4F2-238224723834}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AC320DAA-A040-4654-A4F2-238224723834}.Release|Any CPU.Build.0 = Release|Any CPU + {6D06AE63-9EB8-4FFA-891C-455CB95A23BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6D06AE63-9EB8-4FFA-891C-455CB95A23BD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6D06AE63-9EB8-4FFA-891C-455CB95A23BD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6D06AE63-9EB8-4FFA-891C-455CB95A23BD}.Release|Any CPU.Build.0 = Release|Any CPU + {BC08ACC4-8B61-4801-8532-D5D054795809}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BC08ACC4-8B61-4801-8532-D5D054795809}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BC08ACC4-8B61-4801-8532-D5D054795809}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BC08ACC4-8B61-4801-8532-D5D054795809}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {35CAC4B4-A8ED-4DBA-941D-7E4438178987} + EndGlobalSection +EndGlobal diff --git a/FastVideoDSEncoder/Adpcm.cs b/FastVideoDSEncoder/Adpcm.cs new file mode 100644 index 0000000..baa57e3 --- /dev/null +++ b/FastVideoDSEncoder/Adpcm.cs @@ -0,0 +1,188 @@ +using System; + +namespace Gericom.FastVideoDSEncoder +{ + public static class Adpcm + { + private static readonly int[] IndexTable = + { + -3, -3, -2, -1, 2, 4, 6, 8, + -3, -3, -2, -1, 2, 4, 6, 8 + }; + + private static readonly short[] StepTable = + { + 7, 8, 9, 10, 11, 12, 13, 14, + 16, 17, 19, 21, 23, 25, 28, + 31, 34, 37, 41, 45, 50, 55, + 60, 66, 73, 80, 88, 97, 107, + 118, 130, 143, 157, 173, 190, 209, + 230, 253, 279, 307, 337, 371, 408, + 449, 494, 544, 598, 658, 724, 796, + 876, 963, 1060, 1166, 1282, 1411, 1552, + 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026, + 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, + 9493, 10442, 11487, 12635, 13899, 15289, 16818, + 18500, 20350, 22385, 24623, 27086, 29794, 32767 + }; + + public record AdpcmState(short LastSample, int LastIdx); + + private static int GetBestTableIndex(int diff) + { + int lowestDiff = int.MaxValue; + int lowestIdx = -1; + for (int i = 0; i < StepTable.Length; i++) + { + int diff2 = Math.Abs(Math.Abs(diff) - StepTable[i]); + if (diff2 < lowestDiff) + { + lowestDiff = diff2; + lowestIdx = i; + } + } + + return lowestIdx; + } + + public static byte[] Encode(ReadOnlySpan samples) + => Encode(samples, null, true).data; + + public static (byte[] data, AdpcmState lastState) Encode(ReadOnlySpan samples, AdpcmState state, + bool emitHeader) + { + if ((samples.Length & 7) != 0) + throw new Exception("Samples should be a multiple of 8"); + var result = new byte[4 + samples.Length / 2]; + var resultSpan = result.AsSpan(); + int idx; + short last; + if (state == null) + { + if (!emitHeader) + throw new ArgumentException(nameof(emitHeader)); + idx = Math.Min(GetBestTableIndex(samples[1] - samples[0]) + 3, 88); + last = (short)Math.Max(-0x7FFF, samples[0] - StepTable[idx] / 8); + } + else + { + idx = state.LastIdx; + last = state.LastSample; + } + + int offset = 0; + if (emitHeader) + { + resultSpan.WriteLe(0, last); + resultSpan.WriteLe(2, (ushort)idx); + offset += 4; + } + + int error = 0; + for (int i = 0; i < samples.Length; i += 8) + { + uint nibbles = 0; + for (int j = 0; j < 8; j++) + { + int step = StepTable[idx]; + int sample = samples[i + j]; + + int best = -1; + int bestScore = int.MaxValue; + for (int k = 0; k < 16; k++) + { + int diff2 = (step * ((k & 7) * 2 + 1)) >> 3; + int sample3 = last + diff2 * (((k >> 3) & 1) == 1 ? -1 : 1); + if (sample3 < -0x7FFF) + sample3 = -0x7FFF; + if (sample3 > 0x7FFF) + sample3 = 0x7FFF; + int score = Math.Abs(sample3 - sample); + if (i + j + 1 < samples.Length) + { + int step2 = StepTable[Math.Clamp(idx + IndexTable[k], 0, 88)]; + int bestScore2 = int.MaxValue; + for (int l = 0; l < 16; l++) + { + int diff3 = (step2 * ((l & 7) * 2 + 1)) >> 3; + int sample4 = sample3 + diff3 * (((l >> 3) & 1) == 1 ? -1 : 1); + if (sample4 < -0x7FFF) + sample4 = -0x7FFF; + if (sample4 > 0x7FFF) + sample4 = 0x7FFF; + int score2 = Math.Abs(sample4 - samples[i + j + 1]); + if (score2 < bestScore2) + bestScore2 = score2; + } + + score += bestScore2; + } + + if (score < bestScore) + { + bestScore = score; + best = k; + } + } + + nibbles |= (uint)best << (j * 4); + + int diff = (step * ((best & 7) * 2 + 1)) >> 3; + int sample2 = last + diff * (((best >> 3) & 1) == 1 ? -1 : 1); + if (sample2 < -0x7FFF) + sample2 = -0x7FFF; + if (sample2 > 0x7FFF) + sample2 = 0x7FFF; + last = (short)sample2; + idx += IndexTable[best]; + if (idx < 0) + idx = 0; + if (idx > 88) + idx = 88; + } + + resultSpan.WriteLe(offset, nibbles); + offset += 4; + } + + return (result, new(last, idx)); + } + + public static short[] Decode(ReadOnlySpan samples) + { + if ((samples.Length & 3) != 0) + throw new Exception("Samples should be a multiple of 4 bytes"); + var outSamples = new short[(samples.Length - 4) * 2]; + short last = samples.ReadLe(0); + int idx = samples.ReadLe(2); + int offset = 4; + for (int i = 0; i < outSamples.Length; i += 8) + { + uint nibbles = samples.ReadLe(offset); + offset += 4; + for (int j = 0; j < 8; j++) + { + uint nibble = nibbles & 0xF; + nibbles >>= 4; + int diff = (int)((StepTable[idx] * ((nibble & 7) * 2 + 1)) >> 3); + + int sample = last + diff * (((nibble >> 3) & 1) == 1 ? -1 : 1); + + if (sample < -0x7FFF) + sample = -0x7FFF; + if (sample > 0x7FFF) + sample = 0x7FFF; + outSamples[i + j] = (short)sample; + last = (short)sample; + idx += IndexTable[nibble]; + if (idx < 0) + idx = 0; + if (idx > 88) + idx = 88; + } + } + + return outSamples; + } + } +} \ No newline at end of file diff --git a/FastVideoDSEncoder/FFMpegDecoder.cs b/FastVideoDSEncoder/FFMpegDecoder.cs new file mode 100644 index 0000000..04abb5a --- /dev/null +++ b/FastVideoDSEncoder/FFMpegDecoder.cs @@ -0,0 +1,468 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using FFmpeg.AutoGen; +using Gericom.FastVideoDS.Frames; +using static FFmpeg.AutoGen.ffmpeg; + +namespace Gericom.FastVideoDSEncoder +{ + public unsafe class FFMpegDecoder : IDisposable + { + public const int StreamAuto = -1; + public const int NoStream = -2; + + private AVFormatContext* _formatContext; + private AVCodecContext* _videoDecContext; + private AVCodecContext* _audioDecContext; + private SwsContext* _swsContext; + private SwrContext* _swrContext; + private AVPacket* _packet; + private AVFrame* _frame; + + private readonly byte* _rgbData; + + private readonly Queue _frameQueue = new(); + private readonly FramePool _framePool; + private readonly short[] _leftBuffer = new short[2 * 48000]; + private readonly short[] _rightBuffer = new short[2 * 48000]; + private int _audioSampleCount; + private int _audioReadOffset; + private int _audioWriteOffset; + + private long _curVideoPts = -1; + private long _curAudioPts = -1; + + public long FirstVideoPts { get; private set; } = -1; + public long FirstAudioPktPos { get; private set; } = -1; + + public long MaxAudioPktPos { get; set; } = -1; + + public int VideoStreamId { get; } + public int AudioStreamId { get; } + + public int FrameHeight { get; private set; } + + static FFMpegDecoder() + { + ffmpeg.RootPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "x64"); + } + + public FFMpegDecoder(string srcPath, long startPts = 0, int videoStreamId = -1, int audioStreamId = -1) + { + AVFormatContext* formatContext = null; + if (avformat_open_input(&formatContext, srcPath, null, null) < 0) + throw new Exception("avformat_open_input error"); + + _formatContext = formatContext; + + if (avformat_find_stream_info(_formatContext, null) < 0) + throw new Exception("avformat_find_stream_info error"); + + if (videoStreamId >= 0 && + (videoStreamId >= formatContext->nb_streams || + formatContext->streams[videoStreamId]->codecpar->codec_type != AVMediaType.AVMEDIA_TYPE_VIDEO)) + throw new ArgumentException(nameof(videoStreamId)); + + if (audioStreamId >= 0 && + (audioStreamId >= formatContext->nb_streams || + formatContext->streams[audioStreamId]->codecpar->codec_type != AVMediaType.AVMEDIA_TYPE_AUDIO)) + throw new ArgumentException(nameof(audioStreamId)); + + for (int i = 0; i < formatContext->nb_streams; i++) + { + var codecType = formatContext->streams[i]->codecpar->codec_type; + + if (videoStreamId != NoStream && videoStreamId < 0 && codecType == AVMediaType.AVMEDIA_TYPE_VIDEO) + { + videoStreamId = i; + continue; + } + + if (audioStreamId != NoStream && audioStreamId < 0 && codecType == AVMediaType.AVMEDIA_TYPE_AUDIO) + { + audioStreamId = i; + continue; + } + } + + if (videoStreamId != NoStream && videoStreamId < 0) + throw new Exception("No video stream found"); + + if (audioStreamId != NoStream && audioStreamId < 0) + throw new Exception("No audio stream found"); + + VideoStreamId = videoStreamId; + AudioStreamId = audioStreamId; + + if (VideoStreamId != NoStream) + { + AVCodec* videoDec = avcodec_find_decoder(_formatContext->streams[VideoStreamId]->codecpar->codec_id); + _videoDecContext = avcodec_alloc_context3(videoDec); + avcodec_parameters_to_context(_videoDecContext, _formatContext->streams[VideoStreamId]->codecpar); + avcodec_open2(_videoDecContext, videoDec, null); + } + + if (AudioStreamId != NoStream) + { + AVCodec* audioDec = avcodec_find_decoder(_formatContext->streams[AudioStreamId]->codecpar->codec_id); + _audioDecContext = avcodec_alloc_context3(audioDec); + avcodec_parameters_to_context(_audioDecContext, _formatContext->streams[AudioStreamId]->codecpar); + avcodec_open2(_audioDecContext, audioDec, null); + } + + if (startPts != 0 && VideoStreamId != NoStream) + { + if (av_seek_frame(_formatContext, VideoStreamId, startPts, 0) < 0) + throw new Exception("av_seek_frame failed"); + avcodec_flush_buffers(_videoDecContext); + + if (AudioStreamId != NoStream) + avcodec_flush_buffers(_audioDecContext); + } + + if (VideoStreamId != NoStream) + { + var aspect = _videoDecContext->sample_aspect_ratio; + if (aspect.num == 0 && aspect.den == 1) + aspect.num = 1; + + FrameHeight = (int)Math.Round(_videoDecContext->height * 256.0 / _videoDecContext->width * aspect.den / + aspect.num); + + int mod8 = FrameHeight & 7; + if (mod8 != 0) + { + FrameHeight &= ~7; + if (mod8 >= 4) + FrameHeight += 8; + } + + _framePool = new FramePool(256, FrameHeight); + + _swsContext = sws_getContext(_videoDecContext->width, _videoDecContext->height, + _videoDecContext->pix_fmt, + 256, FrameHeight, AVPixelFormat.AV_PIX_FMT_BGRA, + SWS_LANCZOS | SWS_FULL_CHR_H_INT | SWS_FULL_CHR_H_INP | SWS_ACCURATE_RND, null, null, null); + } + + _packet = av_packet_alloc(); + _frame = av_frame_alloc(); + + _audioSampleCount = 0; + _audioReadOffset = 0; + _audioWriteOffset = 0; + + if (VideoStreamId != NoStream) + { + _rgbData = (byte*)NativeMemory.AlignedAlloc(256 * 192 * 4, 16); + + while (FirstVideoPts == -1 && PumpData()) ; + } + + if (AudioStreamId != NoStream) + { + while (FirstAudioPktPos == -1 && PumpData()) ; + } + } + + public AVRational GetFrameRate() + { + var rate = _formatContext->streams[VideoStreamId]->r_frame_rate; + // if (rate.num == 50 && rate.den == 1 && _formatContext->streams[VideoStreamId]->codecpar->field_order != + // AVFieldOrder.AV_FIELD_PROGRESSIVE) + // rate.num = 25; + return rate; + // return _formatContext->streams[VideoStreamId]->r_frame_rate; + } + + public AVRational GetVideoTimeBase() + { + return _formatContext->streams[VideoStreamId]->time_base; + } + + public long GetDuration() + { + if (_formatContext->streams[VideoStreamId]->duration <= 0) + return _formatContext->duration * _formatContext->streams[VideoStreamId]->time_base.den / + ((long)AV_TIME_BASE * _formatContext->streams[VideoStreamId]->time_base.num); + + return _formatContext->streams[VideoStreamId]->duration; + } + + public int GetAudioRate() + { + return 47605; //_formatContext->streams[AudioStreamId]->codecpar->sample_rate; + } + + private bool ReceiveVideoFrame() + { + if (avcodec_receive_frame(_videoDecContext, _frame) < 0) + return false; + + if (_frame->best_effort_timestamp <= _curVideoPts) + { + av_frame_unref(_frame); + return false; + } + + if (FirstVideoPts == -1) + FirstVideoPts = _frame->best_effort_timestamp; + + _curVideoPts = _frame->best_effort_timestamp; + + // fixed (byte* pRgb = _rgbData) + // { + sws_scale(_swsContext, _frame->data, _frame->linesize, 0, + _videoDecContext->height, new[] { _rgbData }, new[] { 256 * 4 }); + + var refFrame = _framePool.AcquireFrame(); + + refFrame.Frame.FromRgba32(_rgbData, 256 * 4); + _frameQueue.Enqueue(refFrame); + // if(_frame->interlaced_frame != 0) + // _frameQueue.Enqueue(RGB555Frame.FromRgba32(pRgb, 256, FrameHeight, 256 * 4)); + // } + + av_frame_unref(_frame); + return true; + } + + private bool ReceiveAudioFrame() + { + if (avcodec_receive_frame(_audioDecContext, _frame) < 0) + return false; + + if (_frame->best_effort_timestamp <= _curAudioPts) + { + av_frame_unref(_frame); + return false; + } + + if (MaxAudioPktPos != -1 && _frame->pkt_pos >= MaxAudioPktPos) + { + av_frame_unref(_frame); + return false; + } + + if (FirstAudioPktPos == -1) + FirstAudioPktPos = _frame->pkt_pos; + + _curAudioPts = _frame->best_effort_timestamp; + + if (_swrContext == null) + { + var inChLayout = &_frame->ch_layout; + AVChannelLayout outChLayout; + av_channel_layout_from_mask(&outChLayout, AV_CH_LAYOUT_STEREO); + // if (chLayout == 0) + // chLayout = av_get_default_channel_layout(_frame->channels); + fixed (SwrContext** swr = &_swrContext) + { + int result = swr_alloc_set_opts2(swr, + &outChLayout, AVSampleFormat.AV_SAMPLE_FMT_S16P, 47605, + inChLayout, _audioDecContext->sample_fmt, _frame->sample_rate, + 0, null); + if (result < 0) + throw new Exception("swr_alloc_set_opts2 error"); + } + + swr_init(_swrContext); + } + + byte** outBufs = stackalloc byte*[2]; + int outSamples = (int)av_rescale_rnd( + swr_get_delay(_swrContext, _frame->sample_rate) + _frame->nb_samples, 47605, + _frame->sample_rate, AVRounding.AV_ROUND_UP); + + if (_audioWriteOffset + outSamples <= _leftBuffer.Length) + { + fixed (short* leftPtr = _leftBuffer, rightPtr = _rightBuffer) + { + outBufs[0] = (byte*)&leftPtr[_audioWriteOffset]; + outBufs[1] = (byte*)&rightPtr[_audioWriteOffset]; + outSamples = swr_convert(_swrContext, outBufs, outSamples, (byte**)&_frame->data, + _frame->nb_samples); + if (outSamples < 0) + throw new Exception("swr_convert error"); + } + } + else + { + var leftBuf = new short[outSamples]; + var rightBuf = new short[outSamples]; + fixed (short* leftPtr = leftBuf, rightPtr = rightBuf) + { + outBufs[0] = (byte*)leftPtr; + outBufs[1] = (byte*)rightPtr; + outSamples = swr_convert(_swrContext, outBufs, outSamples, (byte**)&_frame->data, + _frame->nb_samples); + if (outSamples < 0) + throw new Exception("swr_convert error"); + } + + int firstHalf = _leftBuffer.Length - _audioWriteOffset; + if (firstHalf > outSamples) + firstHalf = outSamples; + Array.Copy(leftBuf, 0, _leftBuffer, _audioWriteOffset, firstHalf); + Array.Copy(rightBuf, 0, _rightBuffer, _audioWriteOffset, firstHalf); + if (firstHalf != outSamples) + { + int secondHalf = outSamples - firstHalf; + Array.Copy(leftBuf, firstHalf, _leftBuffer, 0, secondHalf); + Array.Copy(rightBuf, firstHalf, _rightBuffer, 0, secondHalf); + } + } + + _audioWriteOffset += outSamples; + if (_audioWriteOffset >= _leftBuffer.Length) + _audioWriteOffset -= _leftBuffer.Length; + + _audioSampleCount += outSamples; + if (_audioSampleCount > _leftBuffer.Length) + throw new Exception("Audio buffer overflow!"); + + av_frame_unref(_frame); + return true; + } + + private bool PumpData() + { + int ret = av_read_frame(_formatContext, _packet); + if (ret == AVERROR_EOF) + { + if (VideoStreamId != NoStream) + { + while (ReceiveVideoFrame()) ; + } + + if (AudioStreamId != NoStream) + { + while (ReceiveAudioFrame()) ; + } + + return false; + } + + if (_packet->stream_index == VideoStreamId) + { + avcodec_send_packet(_videoDecContext, _packet); + ReceiveVideoFrame(); + } + else if (_packet->stream_index == AudioStreamId) + { + avcodec_send_packet(_audioDecContext, _packet); + ReceiveAudioFrame(); + } + + av_packet_unref(_packet); + + return true; + } + + public RefFrame GetNextFrame() + { + if (VideoStreamId == NoStream) + return null; + + while (_frameQueue.Count == 0) + { + if (!PumpData()) + { + if (_frameQueue.Count == 0) + return null; + break; + } + } + + return _frameQueue.Dequeue(); + } + + public int GetAudioSamples(short[] leftDst, short[] rightDst, int count) + { + if (AudioStreamId == NoStream) + return 0; + + if (count > _leftBuffer.Length) + throw new Exception("Requesting too much audio data"); + + while (_audioSampleCount < count) + { + if (!PumpData()) + { + count = _audioSampleCount; + break; + } + } + + if (_audioReadOffset + count <= _leftBuffer.Length) + { + Array.Copy(_leftBuffer, _audioReadOffset, leftDst, 0, count); + Array.Copy(_rightBuffer, _audioReadOffset, rightDst, 0, count); + } + else + { + int firstHalf = _leftBuffer.Length - _audioReadOffset; + Array.Copy(_leftBuffer, _audioReadOffset, leftDst, 0, firstHalf); + Array.Copy(_rightBuffer, _audioReadOffset, rightDst, 0, firstHalf); + int secondHalf = count - firstHalf; + Array.Copy(_leftBuffer, 0, leftDst, firstHalf, secondHalf); + Array.Copy(_rightBuffer, 0, rightDst, firstHalf, secondHalf); + } + + _audioReadOffset += count; + if (_audioReadOffset >= _leftBuffer.Length) + _audioReadOffset -= _leftBuffer.Length; + _audioSampleCount -= count; + return count; + } + + public void Dispose() + { + fixed (AVPacket** packet = &_packet) + av_packet_free(packet); + fixed (AVFrame** frame = &_frame) + av_frame_free(frame); + if (VideoStreamId != NoStream) + { + sws_freeContext(_swsContext); + _swsContext = null; + } + + if (AudioStreamId != NoStream && _swrContext != null) + { + fixed (SwrContext** swrContext = &_swrContext) + swr_free(swrContext); + } + + if (AudioStreamId != NoStream) + { + avcodec_close(_audioDecContext); + fixed (AVCodecContext** audioDecContext = &_audioDecContext) + avcodec_free_context(audioDecContext); + } + + if (VideoStreamId != NoStream) + { + avcodec_close(_videoDecContext); + fixed (AVCodecContext** videoDecContext = &_videoDecContext) + avcodec_free_context(videoDecContext); + } + + fixed (AVFormatContext** formatContext = &_formatContext) + avformat_close_input(formatContext); + + if (_rgbData != null) + NativeMemory.AlignedFree(_rgbData); + + GC.SuppressFinalize(this); + } + + ~FFMpegDecoder() + { + if (_rgbData != null) + NativeMemory.AlignedFree(_rgbData); + } + } +} \ No newline at end of file diff --git a/FastVideoDSEncoder/FastVideoDSEncoder.csproj b/FastVideoDSEncoder/FastVideoDSEncoder.csproj new file mode 100644 index 0000000..025a978 --- /dev/null +++ b/FastVideoDSEncoder/FastVideoDSEncoder.csproj @@ -0,0 +1,87 @@ + + + + Exe + net6.0 + Gericom + FastVideoDS Encoder + disable + $(MSBuildProjectName) + Gericom.FastVideoDSEncoder + + + + true + false + x64 + + + + true + + + + + + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + diff --git a/FastVideoDSEncoder/FvEncoder.cs b/FastVideoDSEncoder/FvEncoder.cs new file mode 100644 index 0000000..c20a08f --- /dev/null +++ b/FastVideoDSEncoder/FvEncoder.cs @@ -0,0 +1,323 @@ +using System; +using System.Buffers.Binary; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.Intrinsics.X86; +using System.Threading; +using System.Threading.Tasks; + +namespace Gericom.FastVideoDSEncoder +{ + public class FvEncoder + { + private const int AudioFrameSize = 256; + + public volatile int[] JobProgess; + + public List<(int frame, uint offset)>[] JobKeyFrames; + + public uint[] JobFileSize; + + public int MaxGopLength = 250; + + private FvEncoder() { } + + private record EncoderParams(int JobId, string DestinationPath, FFMpegDecoder Decoder, int StartFrame, + int EndFrame); + + private void EncoderThreadMain(object encParams) + { + var (jobId, dstPath, decoder, frame, endFrame) = (EncoderParams)encParams; + + var dummyAudioBuf = new byte[4 + (AudioFrameSize / 2)]; + using (var outStream = File.Create(dstPath)) + { + int audioRate = decoder.GetAudioRate(); + var videoRate = decoder.GetFrameRate(); + var encoder = new Gericom.FastVideoDS.FastVideoDSEncoder(256, decoder.FrameHeight, 30, 0, MaxGopLength); + + int inFrame = frame; + int outFrame = frame; + + while (true) + { + if (inFrame < endFrame) + { + var frameData = decoder.GetNextFrame(); + if (frameData != null) + { + encoder.SendFrame(frameData); + if (++inFrame == endFrame) + encoder.Flush(); + } + else + { + inFrame = endFrame; + encoder.Flush(); + } + } + + var encData = encoder.ReceiveFrame(); + if (encData != null) + { + long expectedSamples = (long)audioRate * (outFrame + 1) * videoRate.den / videoRate.num; + long audioSamplesWritten = ((long)audioRate * outFrame * videoRate.den / videoRate.num) / + AudioFrameSize * AudioFrameSize; + int newSamples = (int)(expectedSamples - audioSamplesWritten); + int audioFrames = newSamples > 0 ? newSamples / AudioFrameSize : 0; + + long curPos = outStream.Position; + + int frameLen = (encData.Data.Length + 3) & ~3; + uint sizeField = ((uint)frameLen & 0x1FFFFu) | ((uint)audioFrames << 17); + outStream.WriteByte((byte)(sizeField & 0xFF)); + outStream.WriteByte((byte)((sizeField >> 8) & 0xFF)); + outStream.WriteByte((byte)((sizeField >> 16) & 0xFF)); + outStream.WriteByte((byte)((sizeField >> 24) & 0xFF)); + outStream.Write(encData.Data); + for (int i = encData.Data.Length; i < frameLen; i++) + outStream.WriteByte(0); + + for (int j = 0; j < audioFrames; j++) + { + outStream.Write(dummyAudioBuf); + outStream.Write(dummyAudioBuf); + } + + if (encData.Type == Gericom.FastVideoDS.FastVideoDSEncoder.FvFrameType.IFrame) + JobKeyFrames[jobId].Add((outFrame, (uint)curPos)); + + outFrame++; + + JobProgess[jobId] = outFrame; + } + + if (inFrame == endFrame && encoder.FrameQueueEmpty) + break; + } + + decoder.Dispose(); + + JobFileSize[jobId] = (uint)outStream.Length; + } + } + + public static void Encode(string inFile, string outFile, int jobCount = 1) + { + if (!Avx2.IsSupported) + throw new Exception("Avx2 instruction support not available"); + + if (jobCount < 1) + throw new ArgumentException(nameof(jobCount)); + + var context = new FvEncoder(); + + var decoder = new FFMpegDecoder(inFile); + + int audioStream = decoder.AudioStreamId; + + long duration = decoder.GetDuration(); + + var timeBase = decoder.GetVideoTimeBase(); + var videoRate = decoder.GetFrameRate(); + int audioRate = decoder.GetAudioRate(); + int frameHeight = decoder.FrameHeight; + + decoder.Dispose(); + + long partDuration = duration / jobCount; + + var decoders = new FFMpegDecoder[jobCount]; + + for (int i = 0; i < jobCount; i++) + { + decoders[i] = new FFMpegDecoder(inFile, partDuration * i, decoder.VideoStreamId, + FFMpegDecoder.NoStream); //decoder.AudioStreamId); + // if (i != 0) + // decoders[i - 1].MaxAudioPktPos = decoders[i].FirstAudioPktPos; + } + + int ptsToFrame(long pts) => + (int)(pts * timeBase.num * videoRate.num / ((long)timeBase.den * videoRate.den)); + + context.JobProgess = new int[jobCount]; + context.JobKeyFrames = new List<(int, uint)>[jobCount]; + context.JobFileSize = new uint[jobCount]; + // var jobProgess = new int[jobCount]; + var startFrames = new int[jobCount]; + var endFrames = new int[jobCount]; + + var tasks = new Task[jobCount]; + for (int i = 0; i < jobCount; i++) + { + int startFrame = i == 0 ? 0 : ptsToFrame(decoders[i].FirstVideoPts); + int endFrame = i == jobCount - 1 ? ptsToFrame(duration) : ptsToFrame(decoders[i + 1].FirstVideoPts); + + startFrames[i] = startFrame; + endFrames[i] = endFrame; + context.JobProgess[i] = startFrame; + context.JobKeyFrames[i] = new(); + + tasks[i] = Task.Factory.StartNew(context.EncoderThreadMain, + new EncoderParams(i, $"{outFile}.{i}", decoders[i], startFrame, endFrame)); + } + + Console.WriteLine($"Encoding video with {jobCount} jobs ..."); + var stopwatch = Stopwatch.StartNew(); + while (true) + { + bool allDone = true; + for (int i = 0; i < jobCount; i++) + { + if (!tasks[i].IsCompleted) + { + allDone = false; + break; + } + } + + int consoleWidth = Console.BufferWidth; + int barWidth = consoleWidth - 3; + Console.SetCursorPosition(0, Console.GetCursorPosition().Top); + Console.Write('['); + int totalFrames = endFrames[^1]; + for (int i = 0; i < barWidth; i++) + { + int barFrame = (i + 1) * totalFrames / barWidth; + for (int j = 0; j < jobCount; j++) + { + if (barFrame >= startFrames[j] && barFrame <= endFrames[j]) + { + if (context.JobProgess[j] >= barFrame) + Console.Write('#'); + else + Console.Write('.'); + break; + } + } + } + + Console.Write(']'); + + if (allDone) + { + Console.WriteLine(); + break; + } + + Thread.Sleep(500); + } + + uint keyFrameCount = (uint)context.JobKeyFrames.Sum(j => (uint)j.Count); + + var audioBlockL = new short[AudioFrameSize]; + var audioBlockR = new short[AudioFrameSize]; + + using (var outStream = File.Create(outFile)) + { + var header = new byte[0x1C + keyFrameCount * 8]; + header[0] = (byte)'F'; + header[1] = (byte)'V'; + header[2] = (byte)'D'; + header[3] = (byte)'S'; + var headerSpan = header.AsSpan(); + headerSpan.WriteLe(0x04, 256); + headerSpan.WriteLe(0x06, (ushort)frameHeight); + headerSpan.WriteLe(0x08, (uint)videoRate.num); + headerSpan.WriteLe(0x0C, (uint)videoRate.den); + headerSpan.WriteLe(0x10, (ushort)audioRate); + headerSpan.WriteLe(0x12, 2); + headerSpan.WriteLe(0x14, (uint)endFrames[^1]); + headerSpan.WriteLe(0x18, keyFrameCount); + + uint jobOffset = (uint)header.Length; + int headerOffset = 0x1C; + for (int i = 0; i < jobCount; i++) + { + foreach (var (frame, offset) in context.JobKeyFrames[i]) + { + headerSpan.WriteLe(headerOffset, (uint)frame); + headerSpan.WriteLe(headerOffset + 4, jobOffset + offset); + headerOffset += 8; + } + + jobOffset += context.JobFileSize[i]; + } + + outStream.Write(header); + + for (int i = 0; i < jobCount; i++) + { + using (var partStream = File.OpenRead($"{outFile}.{i}")) + partStream.CopyTo(outStream); + + File.Delete($"{outFile}.{i}"); + } + + stopwatch.Stop(); + Console.WriteLine($"Video encoding done in {stopwatch.Elapsed}"); + + Console.WriteLine("Encoding audio..."); + + stopwatch = Stopwatch.StartNew(); + + var audioDec = new FFMpegDecoder(inFile, 0, FFMpegDecoder.NoStream, audioStream); + + outStream.Position = header.Length; + var sizeBuf = new byte[4]; + + int f = 0; + var audioTask = Task.Factory.StartNew(() => + { + Adpcm.AdpcmState lastLeft = null; + Adpcm.AdpcmState lastRight = null; + for (; f < endFrames[^1]; f++) + { + outStream.Read(sizeBuf); + uint sizeField = BinaryPrimitives.ReadUInt32LittleEndian(sizeBuf); + + uint frameLen = sizeField & 0x1FFFFu; + uint audioFrames = sizeField >> 17; + + outStream.Position += frameLen; + + for (int j = 0; j < audioFrames; j++) + { + Array.Clear(audioBlockL, 0, audioBlockL.Length); + Array.Clear(audioBlockR, 0, audioBlockR.Length); + int samples = audioDec.GetAudioSamples(audioBlockL, audioBlockR, AudioFrameSize); + (var data, lastLeft) = Adpcm.Encode(audioBlockL, lastLeft, true); + outStream.Write(data); + + (data, lastRight) = Adpcm.Encode(audioBlockR, lastRight, true); + outStream.Write(data); + } + } + }); + + while (!audioTask.IsCompleted) + { + int consoleWidth = Console.BufferWidth; + int barWidth = consoleWidth - 3; + Console.SetCursorPosition(0, Console.GetCursorPosition().Top); + Console.Write('['); + int totalFrames = endFrames[^1]; + int done = barWidth * f / totalFrames; + Console.Write(Enumerable.Repeat('#', done).ToArray()); + Console.Write(Enumerable.Repeat('.', barWidth - done).ToArray()); + + Console.Write(']'); + + Thread.Sleep(500); + } + + Console.WriteLine(); + + stopwatch.Stop(); + Console.WriteLine($"Audio encoding done in {stopwatch.Elapsed}"); + } + } + } +} \ No newline at end of file diff --git a/FastVideoDSEncoder/Program.cs b/FastVideoDSEncoder/Program.cs new file mode 100644 index 0000000..e97022b --- /dev/null +++ b/FastVideoDSEncoder/Program.cs @@ -0,0 +1,66 @@ +using System; +using System.Runtime.Intrinsics.X86; +using CommandLine; +using CommandLine.Text; + +namespace Gericom.FastVideoDSEncoder +{ + internal class Program + { + private class Options + { + [Option('j', HelpText = "Number of concurrent jobs (default: cpu threads / 1.5)", MetaValue = "jobs")] + public int? Jobs { get; set; } + + [Value(0, Required = true, MetaName = "input", HelpText = "Input video file")] + public string Input { get; set; } + + [Value(1, Required = true, MetaName = "output", HelpText = "Output video file")] + public string Output { get; set; } + } + + static void Main(string[] args) + { + Console.WriteLine("FastVideoDS Encoder by Gericom"); + + if (!Avx2.IsSupported) + { + Console.WriteLine(); + Console.WriteLine("This encoder requires a cpu with support for AVX2 instructions"); + return; + } + + var parser = new Parser(with => + { + with.HelpWriter = null; + with.AutoVersion = false; + with.AutoHelp = true; + }); + + var parserResult = parser.ParseArguments(args); + + parserResult.WithParsed(opt => + { + Console.WriteLine(); + FvEncoder.Encode(opt.Input, opt.Output, opt.Jobs ?? (int)Math.Round(Environment.ProcessorCount / 1.5)); + }); + + parserResult.WithNotParsed(errs => + { + var helpText = HelpText.AutoBuild(parserResult, h => + { + h.AdditionalNewLineAfterOption = false; + h.Heading = ""; + h.Copyright = ""; + h.AutoVersion = false; + h.AutoHelp = false; + h.AddPreOptionsLine("Usage: FastVideoDSEncoder [-j jobs] input output.fv"); + + return h; + }); + + Console.WriteLine(helpText); + }); + } + } +} \ No newline at end of file diff --git a/FastVideoDSEncoder/Properties/launchSettings.json b/FastVideoDSEncoder/Properties/launchSettings.json new file mode 100644 index 0000000..c429ed6 --- /dev/null +++ b/FastVideoDSEncoder/Properties/launchSettings.json @@ -0,0 +1,7 @@ +{ + "profiles": { + "FastVideoDSEncoder": { + "commandName": "Project" + } + } +} \ No newline at end of file diff --git a/FastVideoDSEncoder/SpanExtensions.cs b/FastVideoDSEncoder/SpanExtensions.cs new file mode 100644 index 0000000..66eb125 --- /dev/null +++ b/FastVideoDSEncoder/SpanExtensions.cs @@ -0,0 +1,140 @@ +using System; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Gericom.FastVideoDSEncoder +{ + public static class SpanExtensions + { + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + private static T ReverseEndianness(T val) where T : unmanaged + { + switch (val) + { + case short value: + return (T)(object)BinaryPrimitives.ReverseEndianness(value); + case ushort value: + return (T)(object)BinaryPrimitives.ReverseEndianness(value); + case int value: + return (T)(object)BinaryPrimitives.ReverseEndianness(value); + case uint value: + return (T)(object)BinaryPrimitives.ReverseEndianness(value); + case long value: + return (T)(object)BinaryPrimitives.ReverseEndianness(value); + case ulong value: + return (T)(object)BinaryPrimitives.ReverseEndianness(value); + default: + return val; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + public static T ReadLe(this Span span, nint offset) where T : unmanaged + => ReadLe(ref MemoryMarshal.GetReference(span), offset); + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + public static T ReadLe(this ReadOnlySpan span, nint offset) where T : unmanaged + => ReadLe(ref MemoryMarshal.GetReference(span), offset); + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + private static T ReadLe(ref byte refSpan, nint offset) where T : unmanaged + { + if (typeof(T) == typeof(float)) + return (T)(object)BinaryPrimitives.ReadSingleLittleEndian( + MemoryMarshal.CreateSpan(ref Unsafe.Add(ref refSpan, offset), sizeof(float))); + + if (typeof(T) == typeof(double)) + return (T)(object)BinaryPrimitives.ReadDoubleLittleEndian( + MemoryMarshal.CreateSpan(ref Unsafe.Add(ref refSpan, offset), sizeof(double))); + + T result = Unsafe.ReadUnaligned(ref Unsafe.Add(ref refSpan, offset)); + + if (BitConverter.IsLittleEndian) + return result; + + return ReverseEndianness(result); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + public static T ReadBe(this Span span, nint offset) where T : unmanaged + => ReadBe(ref MemoryMarshal.GetReference(span), offset); + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + public static T ReadBe(this ReadOnlySpan span, nint offset) where T : unmanaged + => ReadBe(ref MemoryMarshal.GetReference(span), offset); + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + private static T ReadBe(ref byte refSpan, nint offset) where T : unmanaged + { + if (typeof(T) == typeof(float)) + return (T)(object)BinaryPrimitives.ReadSingleBigEndian( + MemoryMarshal.CreateSpan(ref Unsafe.Add(ref refSpan, offset), sizeof(float))); + + if (typeof(T) == typeof(double)) + return (T)(object)BinaryPrimitives.ReadDoubleBigEndian( + MemoryMarshal.CreateSpan(ref Unsafe.Add(ref refSpan, offset), sizeof(double))); + + T result = Unsafe.ReadUnaligned(ref Unsafe.Add(ref refSpan, offset)); + + if (!BitConverter.IsLittleEndian) + return result; + + return ReverseEndianness(result); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + private static void WriteLe(ref byte refSpan, nint offset, T value) where T : unmanaged + { + if (value is float f) + { + BinaryPrimitives.WriteSingleLittleEndian( + MemoryMarshal.CreateSpan(ref Unsafe.Add(ref refSpan, offset), sizeof(float)), f); + return; + } + + if (value is double d) + { + BinaryPrimitives.WriteDoubleLittleEndian( + MemoryMarshal.CreateSpan(ref Unsafe.Add(ref refSpan, offset), sizeof(double)), d); + return; + } + + if (!BitConverter.IsLittleEndian) + value = ReverseEndianness(value); + + Unsafe.WriteUnaligned(ref Unsafe.Add(ref refSpan, offset), value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + public static void WriteLe(this Span span, int offset, T value) where T : unmanaged + => WriteLe(ref MemoryMarshal.GetReference(span), offset, value); + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + private static void WriteBe(ref byte refSpan, nint offset, T value) where T : unmanaged + { + if (value is float f) + { + BinaryPrimitives.WriteSingleBigEndian( + MemoryMarshal.CreateSpan(ref Unsafe.Add(ref refSpan, offset), sizeof(float)), f); + return; + } + + if (value is double d) + { + BinaryPrimitives.WriteDoubleBigEndian( + MemoryMarshal.CreateSpan(ref Unsafe.Add(ref refSpan, offset), sizeof(double)), d); + return; + } + + if (BitConverter.IsLittleEndian) + value = ReverseEndianness(value); + + Unsafe.WriteUnaligned(ref Unsafe.Add(ref refSpan, offset), value); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + public static void WriteBe(this Span span, int offset, T value) where T : unmanaged + => WriteBe(ref MemoryMarshal.GetReference(span), offset, value); + } +} \ No newline at end of file diff --git a/FastVideoDSEncoder/x64/avcodec-59.dll b/FastVideoDSEncoder/x64/avcodec-59.dll new file mode 100644 index 0000000..6fd1048 Binary files /dev/null and b/FastVideoDSEncoder/x64/avcodec-59.dll differ diff --git a/FastVideoDSEncoder/x64/avdevice-59.dll b/FastVideoDSEncoder/x64/avdevice-59.dll new file mode 100644 index 0000000..91fbc59 Binary files /dev/null and b/FastVideoDSEncoder/x64/avdevice-59.dll differ diff --git a/FastVideoDSEncoder/x64/avfilter-8.dll b/FastVideoDSEncoder/x64/avfilter-8.dll new file mode 100644 index 0000000..0dd409c Binary files /dev/null and b/FastVideoDSEncoder/x64/avfilter-8.dll differ diff --git a/FastVideoDSEncoder/x64/avformat-59.dll b/FastVideoDSEncoder/x64/avformat-59.dll new file mode 100644 index 0000000..8daa421 Binary files /dev/null and b/FastVideoDSEncoder/x64/avformat-59.dll differ diff --git a/FastVideoDSEncoder/x64/avutil-57.dll b/FastVideoDSEncoder/x64/avutil-57.dll new file mode 100644 index 0000000..83c86a1 Binary files /dev/null and b/FastVideoDSEncoder/x64/avutil-57.dll differ diff --git a/FastVideoDSEncoder/x64/postproc-56.dll b/FastVideoDSEncoder/x64/postproc-56.dll new file mode 100644 index 0000000..521a7f8 Binary files /dev/null and b/FastVideoDSEncoder/x64/postproc-56.dll differ diff --git a/FastVideoDSEncoder/x64/swresample-4.dll b/FastVideoDSEncoder/x64/swresample-4.dll new file mode 100644 index 0000000..512cfe6 Binary files /dev/null and b/FastVideoDSEncoder/x64/swresample-4.dll differ diff --git a/FastVideoDSEncoder/x64/swscale-6.dll b/FastVideoDSEncoder/x64/swscale-6.dll new file mode 100644 index 0000000..0db7d3d Binary files /dev/null and b/FastVideoDSEncoder/x64/swscale-6.dll differ diff --git a/FastVideoDSInfo/FastVideoDSInfo.csproj b/FastVideoDSInfo/FastVideoDSInfo.csproj new file mode 100644 index 0000000..7950cb0 --- /dev/null +++ b/FastVideoDSInfo/FastVideoDSInfo.csproj @@ -0,0 +1,11 @@ + + + + Exe + net6.0 + disable + disable + Gericom.FastVideoDSInfo + + + diff --git a/FastVideoDSInfo/Program.cs b/FastVideoDSInfo/Program.cs new file mode 100644 index 0000000..e0c2c1f --- /dev/null +++ b/FastVideoDSInfo/Program.cs @@ -0,0 +1,36 @@ +using System; +using System.Buffers.Binary; +using System.IO; + +namespace Gericom.FastVideoDSInfo +{ + internal class Program + { + static void Main(string[] args) + { + using var stream = File.OpenRead(args[0]); + stream.Position += 0x20; + var keyFrame0OffsBuf = new byte[4]; + stream.Read(keyFrame0OffsBuf); + stream.Position = BinaryPrimitives.ReadUInt32LittleEndian(keyFrame0OffsBuf); + using var csvStream = File.CreateText(args[1]); + csvStream.WriteLine("frame;type;size;audioBlocks"); + var frameHeaderBuf = new byte[4]; + int frame = 0; + while (true) + { + if (stream.Read(frameHeaderBuf) < 4) + break; + uint frameHeader = BinaryPrimitives.ReadUInt32LittleEndian(frameHeaderBuf); + uint frameLen = frameHeader & 0x1FFFFu; + uint audioFrames = frameHeader >> 17; + stream.Read(frameHeaderBuf.AsSpan(0, 2)); + bool isPFrame = (BinaryPrimitives.ReadUInt16LittleEndian(frameHeaderBuf) >> 15) == 1; + csvStream.WriteLine($"{frame};{(isPFrame ? "P" : "I")};{frameLen};{audioFrames}"); + stream.Position += frameLen - 2; + stream.Position += audioFrames * 132 * 2; + frame++; + } + } + } +} \ No newline at end of file diff --git a/FastVideoDSInfo/Properties/launchSettings.json b/FastVideoDSInfo/Properties/launchSettings.json new file mode 100644 index 0000000..58aaaae --- /dev/null +++ b/FastVideoDSInfo/Properties/launchSettings.json @@ -0,0 +1,7 @@ +{ + "profiles": { + "FastVideoDSInfo": { + "commandName": "Project" + } + } +} \ No newline at end of file diff --git a/Gericom.FastVideoDS/Bitstream/BitReader.cs b/Gericom.FastVideoDS/Bitstream/BitReader.cs new file mode 100644 index 0000000..fc7b07c --- /dev/null +++ b/Gericom.FastVideoDS/Bitstream/BitReader.cs @@ -0,0 +1,69 @@ +using System; +using System.Buffers.Binary; +using System.Numerics; + +namespace Gericom.FastVideoDS.Bitstream +{ + public class BitReader + { + private readonly byte[] _data; + private int _offset; + public int BitsRemaining; + public uint Bits; + + public BitReader(byte[] data) + { + _data = data; + Bits = (uint)(BinaryPrimitives.ReadUInt16LittleEndian(data) << 16); + BitsRemaining = 0; + _offset = 2; + } + + public uint ReadUnsignedVarInt() + { + int clz = BitOperations.LeadingZeroCount(Bits); //nr zeros + Bits <<= clz; //remove the zeros + Bits += Bits; //remove the stop bit + int r9 = 32 - clz; //shift amount + uint r6 = r9 == 32 ? 0 : Bits >> r9; + r9 = 1; + r6 += (uint)(r9 << clz); + r6--; + Bits <<= clz; + BitsRemaining -= clz << 1; + if (--BitsRemaining < 0) + FillBits(); + return r6; + } + + public void FillBits() + { + if (_offset >= _data.Length) + return; + uint r10 = BinaryPrimitives.ReadUInt16LittleEndian(_data.AsSpan(_offset)); + _offset += 2; + BitsRemaining += 0x10; + int r9 = 0x10 - BitsRemaining; + Bits |= r10 << r9; + } + + private int ReadSignedVarInt() + { + int clz = BitOperations.LeadingZeroCount(Bits); + Bits <<= clz; + Bits += Bits; + int r9 = 32 - clz; + int r6 = r9 == 32 ? 0 : (int)(Bits >> r9); + r9 = 1; + r6 += r9 << clz; + if ((r6 & 1) != 0) + r6 = 1 - r6; + r6 >>= 1; + Bits <<= clz; + BitsRemaining -= clz << 1; + if (--BitsRemaining < 0) + FillBits(); + return r6; + } + } +} \ No newline at end of file diff --git a/Gericom.FastVideoDS/Bitstream/BitWriter.cs b/Gericom.FastVideoDS/Bitstream/BitWriter.cs new file mode 100644 index 0000000..2c29e05 --- /dev/null +++ b/Gericom.FastVideoDS/Bitstream/BitWriter.cs @@ -0,0 +1,99 @@ +using System.IO; +using System.Numerics; + +namespace Gericom.FastVideoDS.Bitstream +{ + public class BitWriter + { + private readonly MemoryStream _stream = new(); + + private uint _bits = 0; + private int _bitCount = 0; + + public void WriteBits(uint value, int nrBits) + { + if (nrBits <= 0) + return; + _bits |= (value & (1u << nrBits) - 1) << 32 - nrBits - _bitCount; + _bitCount += nrBits; + if (_bitCount >= 16) + Flush(); + } + + //Elias gamma coding + public void WriteUnsignedVarInt(uint value) + { + int NrBits = 32 - BitOperations.LeadingZeroCount((value + 1) / 2); + WriteBits(0, NrBits); + WriteBits(1, 1); //stop bit + value -= (1u << NrBits) - 1; + WriteBits(value, NrBits); + } + + public void WriteSignedVarInt(int value) + { + uint val; + if (value <= 0) + val = (uint)(1 - value * 2); + else + val = (uint)(value * 2); + int nrBits = 32 - BitOperations.LeadingZeroCount(val / 2); + WriteBits(0, nrBits); + WriteBits(1, 1); //stop bit + val -= 1u << nrBits; + WriteBits(val, nrBits); + } + + public void Flush() + { + if (_bitCount <= 0) + return; + _stream.WriteByte((byte)(_bits >> 16 & 0xFF)); + _stream.WriteByte((byte)(_bits >> 24 & 0xFF)); + _bitCount -= 16; + _bits <<= 16; + } + + // public byte[] PeekStream() + // { + // if (BitCount <= 0) + // return _stream.ToArray(); + // var res = new List(); + // res.AddRange(_stream.GetBuffer()); + // res.Add((byte)((Bits >> 16) & 0xFF)); + // res.Add((byte)((Bits >> 24) & 0xFF)); + // return res.ToArray(); + // } + + public byte[] ToArray() + { + Flush(); + return _stream.ToArray(); + } + + public static int GetUnsignedVarIntBitCount(uint value) + { + int result = 0; + int nrBits = 32 - BitOperations.LeadingZeroCount((value + 1) / 2); + result += nrBits; + result++; //stop bit + result += nrBits; + return result; + } + + public static int GetSignedVarIntBitCount(int value) + { + int result = 0; + uint val; + if (value <= 0) + val = (uint)(1 - value * 2); + else + val = (uint)(value * 2); + int nrBits = 32 - BitOperations.LeadingZeroCount(val / 2); + result += nrBits; + result++; //stop bit + result += nrBits; + return result; + } + } +} \ No newline at end of file diff --git a/Gericom.FastVideoDS/Dct.cs b/Gericom.FastVideoDS/Dct.cs new file mode 100644 index 0000000..f61a335 --- /dev/null +++ b/Gericom.FastVideoDS/Dct.cs @@ -0,0 +1,34 @@ +using System; + +namespace Gericom.FastVideoDS +{ + public static class Dct + { + public static void Dct4NoDiv(int[] pixels, int[] dst) + { + int t0 = pixels[0] + pixels[1]; + int t1 = pixels[0] - pixels[1]; + int t2 = pixels[2] + pixels[3]; + int t3 = pixels[2] - pixels[3]; + + dst[0] = t0 + t2; + dst[1] = t0 - t2; + dst[2] = t1 + t3; + dst[3] = t1 - t3; + } + + public static void IDct4(int[] dct, byte[] pixels, byte[] dst) + { + int r0 = dct[0] + 16; + int t0 = r0 + dct[1]; + int t2 = r0 - dct[1]; + int t1 = dct[2] + dct[3]; + int t3 = dct[2] - dct[3]; + + dst[0] = (byte)(Math.Clamp((pixels[0] >> 3) + (t0 + t1 >> 5), 0, 31) << 3); + dst[1] = (byte)(Math.Clamp((pixels[1] >> 3) + (t0 - t1 >> 5), 0, 31) << 3); + dst[2] = (byte)(Math.Clamp((pixels[2] >> 3) + (t2 + t3 >> 5), 0, 31) << 3); + dst[3] = (byte)(Math.Clamp((pixels[3] >> 3) + (t2 - t3 >> 5), 0, 31) << 3); + } + } +} \ No newline at end of file diff --git a/Gericom.FastVideoDS/FastVideoDSEncoder.cs b/Gericom.FastVideoDS/FastVideoDSEncoder.cs new file mode 100644 index 0000000..261eda2 --- /dev/null +++ b/Gericom.FastVideoDS/FastVideoDSEncoder.cs @@ -0,0 +1,1998 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; +using Gericom.FastVideoDS.Bitstream; +using Gericom.FastVideoDS.Frames; +using Gericom.FastVideoDS.Utils; + +namespace Gericom.FastVideoDS +{ + public class FastVideoDSEncoder + { + public enum FvFrameType + { + IFrame, + PFrame, + BFrame + } + + public int Width { get; } + public int Height { get; } + + private int _q; + private bool _prevDataValid; + + private RefFrame _lastFrame; + private int _oldQ; + + private RefFrame _backRefFrame; + private RefFrame _forwardRefFrame; + + private readonly FramePool _framePool; + + private readonly Queue<(RefFrame frame, bool forceIFrame)> _frameQueue = new(); + private readonly Queue _bQueue = new(); + + private int _frameNumber = 0; + + private readonly int _maxBLength; + private readonly int _maxGopLength; + + private int _gopLength; + + private bool _flushing = false; + + public bool FrameQueueEmpty => _frameQueue.Count == 0; + + private const float _lambda = 0.35f; + + private const float IBlockRatio = 0.6f; + + public FastVideoDSEncoder(int width, int height, int q, int maxBLength = 0, int maxGopLength = 250) + { + if (width <= 0 || (width & 0x7) != 0) + throw new ArgumentOutOfRangeException(nameof(width), width, "Width should be > 0 and divisible by 8"); + if (height <= 0 || (height & 0x7) != 0) + throw new ArgumentOutOfRangeException(nameof(height), height, + "Height should be > 0 and divisible by 8"); + // if (q < 12 || q > 52) + // throw new ArgumentOutOfRangeException(nameof(q), q, "For q should hold 12 <= q <= 52"); + + if (maxBLength < 0) + throw new ArgumentOutOfRangeException(nameof(maxBLength), maxBLength, + "maxBLength should be greater than or equal to zero"); + + if (maxBLength != 0) + throw new NotSupportedException("B frames are currently not supported"); + + Width = width; + Height = height; + + _maxBLength = maxBLength; + _maxGopLength = maxGopLength; + + _framePool = new FramePool(Width, Height); + + _prevDataValid = false; + + _q = q; + _oldQ = q; + } + + private static readonly int[] QTab4 = + { + 262144 / 32, 262144 / 23, + 262144 / 23, 262144 / 64, + }; + + private static readonly int[] DeQTab4 = + { + 32, 23, + 23, 64 + }; + + private static readonly int[] QTab4P = + { + 262144 / 32, 262144 / 23, + 262144 / 23, 262144 / 64 + }; + + private static readonly int[] DeQTab4P = + { + 32, 23, + 23, 64 + }; + + // private static int[] QTab4 = + // { + // 262144 / 64, 262144 / 46, + // 262144 / 46, 262144 / 128, + // }; + // + // private static int[] DeQTab4 = + // { + // 64, 46, + // 46, 128 + // }; + + // private static int[] QTab4P = + // { + // 262144 / 32, 262144 / 36, + // 262144 / 36, 262144 / 48, + // }; + // + // private static int[] DeQTab4P = + // { + // 32, 36, + // 36, 48 + // }; + + private static bool Quantize4(int[] dct, int[] dst, int[] dstReconstructed) + { + for (int i = 0; i < 4; i++) + { + int f = Math.Min(((1 << 17) + (QTab4[i] >> 1)) / QTab4[i], + (((32 - 11) << 12) + (QTab4[i] >> 1)) / QTab4[i]); + int quant = QTab4[i]; + if (dct[i] < 0) + dst[i] = -(((f - dct[i]) * quant) >> 18); + else + dst[i] = ((f + dct[i]) * quant) >> 18; + } + + int nrZeros = 0; + for (int i = 0; i < 4; i++) + { + if (dst[i] == 0) + nrZeros++; + else + nrZeros = 0; + } + + for (int i = 0; i < 4; i++) + dstReconstructed[i] = dst[i] * DeQTab4[i]; + + return nrZeros == 4; + } + + //based on dct_quantize_trellis_c in libavcodec/mpegvideo_enc.c + private static int QuantizeCombTrellis(int[][] dcts, int[] dst) + { + for (int j = 0; j < 4; j++) + for (int i = 0; i < 16; i++) + dst[j * 16 + i] = dcts[i][j]; + + uint bias = 0; + var runTab = new int[65]; + var levelTab = new int[65]; + var scoreTab = new int[65]; + var survivor = new int[65]; + int lastRun = 0; + int lastLevel = 0; + int lastScore = 0; + var coeff = new int[2 * 64]; + var coeffCount = new int[64]; + const int escLength = 28; + const int lambda = 200; //3;//8; + + int startI = 0; + int lastNonZero = -1; + int lastI = startI; + + uint threshold1 = (1u << 18) - bias - 1u; + uint threshold2 = (threshold1 << 1); + + for (int i = 63; i >= startI; i--) + { + int level = dst[i] * QTab4P[i >> 4]; + + if ((uint)(level + threshold1) > threshold2) + { + lastNonZero = i; + break; + } + } + + for (int i = startI; i <= lastNonZero; i++) + { + int level = dst[i] * QTab4P[i >> 4]; + + if ((uint)(level + threshold1) > threshold2) + { + if (level > 0) + { + level = (int)((bias + level) >> 18); + coeff[i] = level; + coeff[64 + i] = level - 1; + } + else + { + level = (int)((bias - level) >> 18); + coeff[i] = -level; + coeff[64 + i] = -level + 1; + } + + coeffCount[i] = Math.Min(level, 2); + } + else + { + coeff[i] = (level >> 31) | 1; + coeffCount[i] = 1; + } + } + + if (lastNonZero < startI) + { + Array.Clear(dst, startI, 64 - startI); + return lastNonZero; + } + + scoreTab[startI] = 0; + survivor[0] = startI; + int survivorCount = 1; + + for (int i = startI; i <= lastNonZero; i++) + { + int dctCoeff = Math.Abs(dst[i]); + int bestScore = 256 * 256 * 256 * 120; + + int zeroDistortion = dctCoeff * dctCoeff; + + for (int levelIndex = 0; levelIndex < coeffCount[i]; levelIndex++) + { + int level = coeff[levelIndex * 64 + i]; + int alevel = Math.Abs(level); + + int unquantCoeff = alevel * DeQTab4P[i >> 4]; + + int distortion = (unquantCoeff - dctCoeff) * (unquantCoeff - dctCoeff) - zeroDistortion; + level += 64; + if ((level & (~127)) == 0) + { + for (int j = survivorCount - 1; j >= 0; j--) + { + int run = i - survivor[j]; + int score = distortion + Vlc.BitLengthTable[run * 128 + level] * lambda; + score += scoreTab[i - run]; + + if (score < bestScore) + { + bestScore = score; + runTab[i + 1] = run; + levelTab[i + 1] = level - 64; + } + } + + for (int j = survivorCount - 1; j >= 0; j--) + { + int run = i - survivor[j]; + int score = distortion + Vlc.BitLengthTable[64 * 128 + run * 128 + level] * lambda; + score += scoreTab[i - run]; + if (score < lastScore) + { + lastScore = score; + lastRun = run; + lastLevel = level - 64; + lastI = i + 1; + } + } + } + else + { + distortion += escLength * lambda; + for (int j = survivorCount - 1; j >= 0; j--) + { + int run = i - survivor[j]; + int score = distortion + scoreTab[i - run]; + + if (score < bestScore) + { + bestScore = score; + runTab[i + 1] = run; + levelTab[i + 1] = level - 64; + } + } + + for (int j = survivorCount - 1; j >= 0; j--) + { + int run = i - survivor[j]; + int score = distortion + scoreTab[i - run]; + if (score < lastScore) + { + lastScore = score; + lastRun = run; + lastLevel = level - 64; + lastI = i + 1; + } + } + } + } + + scoreTab[i + 1] = bestScore; + + if (lastNonZero <= 27) + { + for (; survivorCount != 0; survivorCount--) + { + if (scoreTab[survivor[survivorCount - 1]] <= bestScore) + break; + } + } + else + { + for (; survivorCount != 0; survivorCount--) + { + if (scoreTab[survivor[survivorCount - 1]] <= bestScore + lambda) + break; + } + } + + survivor[survivorCount++] = i + 1; + } + + int dc = Math.Abs(dst[0]); + lastNonZero = lastI - 1; + Array.Clear(dst, startI, 64 - startI); + + if (lastNonZero < startI) + return lastNonZero; + + if (lastNonZero == 0 && startI == 0) + { + int bestLevel = 0; + int bestScore = dc * dc; + + for (int i = 0; i < coeffCount[0]; i++) + { + int level = coeff[i * 64]; + int alevel = Math.Abs(level); + int score; + + int unquantCoeff = (alevel * DeQTab4P[i >> 4]) >> 3; + unquantCoeff = (unquantCoeff + 4) >> 3; + unquantCoeff <<= 3 + 3; + + int distortion = (unquantCoeff - dc) * (unquantCoeff - dc); + level += 64; + if (level == 0 + 64) + score = distortion; + else if ((level & (~127)) == 0) + score = distortion + Vlc.BitLengthTable[64 * 128 + level] * lambda; + else + score = distortion + escLength * lambda; + + if (score < bestScore) + { + bestScore = score; + bestLevel = level - 64; + } + } + + dst[0] = bestLevel; + if (bestLevel == 0) + return -1; + else + return lastNonZero; + } + + int i2 = lastI; + + dst[lastNonZero] = lastLevel; + i2 -= lastRun + 1; + + for (; i2 > startI; i2 -= runTab[i2] + 1) + dst[i2 - 1] = levelTab[i2]; + + return lastNonZero; + } + + private static int QuantizeComb4PRD(int[][] dcts, int[] dst, int[][] reconstructed) + { + int lastZero = QuantizeCombTrellis(dcts, dst); + for (int j = 0; j < 4; j++) + for (int i = 0; i < 16; i++) + reconstructed[i][j] = dst[j * 16 + i] * DeQTab4P[j]; + if (lastZero < 0) + return 1; + return 1 + Vlc.CalcDctBitCount(dst, lastZero); + } + + private static unsafe (ulong sadI, ulong sadP) SadIP(ReadOnlySpan original, ReadOnlySpan iFrame, + ReadOnlySpan pFrame) + { + var sadI = Vector256.Zero; + var sadP = Vector256.Zero; + fixed (byte* pOriginal0 = original, pIFrame0 = iFrame, pPFrame0 = pFrame) + { + for (int i = 0; i < original.Length; i += 32) + { + var originalPixels = Avx.LoadVector256(pOriginal0 + i); + var iPixels = Avx.LoadVector256(pIFrame0 + i); + var pPixels = Avx.LoadVector256(pPFrame0 + i); + var sadI2 = Avx2.SumAbsoluteDifferences(originalPixels, iPixels); + sadI = Avx2.Add(sadI, sadI2.AsUInt64()); + var sadP2 = Avx2.SumAbsoluteDifferences(originalPixels, pPixels); + sadP = Avx2.Add(sadP, sadP2.AsUInt64()); + } + } + + var resultI = Sse2.Add(sadI.GetLower(), sadI.GetUpper()); + var resultP = Sse2.Add(sadP.GetLower(), sadP.GetUpper()); + return (resultI.GetElement(0) + resultI.GetElement(1), resultP.GetElement(0) + resultP.GetElement(1)); + } + + private unsafe void GxAverage(ReadOnlySpan srcA, ReadOnlySpan srcB, Span dst) + { + fixed (byte* pA = srcA, pB = srcB, pDst = dst) + { + var bit0 = Vector256.Create((short)(1 << 2)); + for (int i = 0; i < srcA.Length; i += 8) + { + ulong row = *(ulong*)(pA + i); + ulong row2 = *(ulong*)(pB + i); + + var a = Avx2.ConvertToVector256Int16(Vector128.Create(row, row2).AsByte()); + var isZero = Avx2.CompareEqual(a, Vector256.Zero); + a = Avx2.Add(a, bit0); + a = Avx2.AndNot(isZero, a); + var b = Sse2.Add(a.GetLower(), a.GetUpper()); + b = Sse2.ShiftRightLogical(b, 4); + b = Sse2.ShiftLeftLogical(b, 3); + *(ulong*)(pDst + i) = Sse2.PackUnsignedSaturate(b, Vector128.Zero).AsUInt64().ToScalar(); + } + } + } + + public record EncFrame(byte[] Data, RefFrame DecFrame, FvFrameType Type, int FrameNumber); + + public void Flush() + { + _flushing = true; + } + + public void SendFrame(RefFrame frame, bool forceIFrame = false) + { + if (_flushing) + throw new Exception(); + + _frameQueue.Enqueue((frame, forceIFrame)); + } + + public EncFrame ReceiveFrame() + { + if (_frameQueue.Count == 0) + return null; + + int iFrameThreshold = (int)((Width / 8) * (Height / 8) * IBlockRatio); + + RefFrame frame; + + byte[] data = null; + RefFrame decFrame = null; + + if (_bQueue.Count > 0) + { + if (_backRefFrame == null || _forwardRefFrame == null) + throw new Exception(); + + frame = _bQueue.Dequeue(); + + throw new NotImplementedException(); + // (data, decFrame, _) = EncodeBFrame(frame.Frame, _backRefFrame.Frame, _forwardRefFrame.Frame); + // var p = EncodePFrame(frame.Frame, _backRefFrame.Frame); + // + // var (sadBR, sadPR) = SadIP(frame.Frame.R, decFrame.Frame.R, p.resultFrame.Frame.R); + // var (sadBG, sadPG) = SadIP(frame.Frame.G, decFrame.Frame.G, p.resultFrame.Frame.G); + // var (sadBB, sadPB) = SadIP(frame.Frame.B, decFrame.Frame.B, p.resultFrame.Frame.B); + // + // ulong sadB = sadBR + sadBG + sadBB; + // ulong sadP = sadPR + sadPG + sadPB; + + frame.Unref(); + + _backRefFrame.Unref(); + _backRefFrame = decFrame; + _gopLength++; + + var result = new EncFrame(data, decFrame, FvFrameType.BFrame, _frameNumber++); + if (_bQueue.Count == 0) + { + _backRefFrame.Unref(); + _backRefFrame = _forwardRefFrame; + _forwardRefFrame = null; + _frameNumber++; //skip over the forward reference + _gopLength++; + } + + return result; + } + + if (_backRefFrame == null || _frameQueue.Peek().forceIFrame || _gopLength >= _maxGopLength) + { + (frame, _) = _frameQueue.Dequeue(); + (data, decFrame) = EncodeIFrame(frame.Frame); + frame.Unref(); + _backRefFrame?.Unref(); + _backRefFrame = decFrame; + _gopLength = 0; + return new(data, decFrame, FvFrameType.IFrame, _frameNumber++); + } + + if (_backRefFrame != null && _forwardRefFrame == null) + { + if (_frameQueue.Count < _maxBLength + 1 && !_flushing) + return null; + + int maxCount = Math.Min(_frameQueue.Count, _maxBLength + 1); + if (_gopLength + maxCount > _maxGopLength) + maxCount = _maxGopLength - _gopLength; + + var frames = new RefFrame[maxCount]; + int count = 0; + for (int i = 0; i < maxCount; i++) + { + var f = _frameQueue.Peek(); + if (f.forceIFrame) + break; + + var (newResultData, newResultFrame, iBlockCount) = EncodePFrame(f.frame.Frame, _backRefFrame.Frame); + + if (iBlockCount > iFrameThreshold) + { + newResultFrame.Unref(); + break; + } + + data = newResultData; + decFrame?.Unref(); + decFrame = newResultFrame; + + frames[i] = f.frame; + _frameQueue.Dequeue(); + count++; + } + + if (count == 0) + { + //Next frame will be an I frame + (frame, _) = _frameQueue.Dequeue(); + (data, decFrame) = EncodeIFrame(frame.Frame); + frame.Unref(); + _backRefFrame?.Unref(); + _backRefFrame = decFrame; + _gopLength = 0; + + return new(data, decFrame, FvFrameType.IFrame, _frameNumber++); + } + else if (count == 1) + { + //Frame becomes regular P frame, and we have the data ready too + frames[0].Unref(); + _backRefFrame?.Unref(); + _backRefFrame = decFrame; + _gopLength++; + + return new(data, decFrame, FvFrameType.PFrame, _frameNumber++); + } + else + { + //count-1 B frames + 1 P frame + for (int i = 0; i < count - 1; i++) + _bQueue.Enqueue(frames[i]); + + frames[count - 1].Unref(); + _forwardRefFrame = decFrame; + + return new(data, decFrame, FvFrameType.PFrame, _frameNumber + _bQueue.Count); + } + } + + return null; + } + + private (byte[] resultData, RefFrame resultFrame) EncodeIFrame(Rgb555Frame frame) + { + if (frame.Width != Width) + throw new Exception("Invalid frame width!"); + if (frame.Height != Height) + throw new Exception("Invalid frame height!"); + + var decRefFrame = _framePool.AcquireFrame(); + var decFrame = decRefFrame.Frame; + var bw = new BitWriter(); + bw.WriteBits(0, 1); //0 = I frame, 1 = P frame + bw.WriteBits((uint)_q, 6); + + var quantsR = new int[16][]; + var quantsG = new int[16][]; + var quantsB = new int[16][]; + var deQuants = new int[16][]; + for (int i = 0; i < 16; i++) + { + quantsR[i] = new int[4]; + quantsG[i] = new int[4]; + quantsB[i] = new int[4]; + deQuants[i] = new int[4]; + } + + var data = new byte[4]; + var dataG = new byte[4]; + var intData = new int[4]; + var dct = new int[4]; + var undct = new byte[4]; + + int lastGDC = 0; + for (int y = 0; y < Height; y += 8) + { + for (int x = 0; x < Width; x += 8) + { + int i = 0; + for (int y3 = 0; y3 < 8; y3 += 4) + { + for (int x3 = 0; x3 < 8; x3 += 4) + { + for (int y2 = 0; y2 < 2; y2++) + { + for (int x2 = 0; x2 < 2; x2++) + { + FrameUtil.GetTile2x2Step2(frame.G, frame.Width, x + x2 + x3, y + y2 + y3, data); + if ((x3 + x2) == 0 && (y3 + y2) == 0) + Array.Clear(dataG, 0, 4); + else if (x2 == 0 && y2 != 0) + FrameUtil.GetTile2x2Step2( + decFrame.G, decFrame.Width, x + x3, y + y2 + y3 - 1, dataG); + else if (x2 == 0 && x3 != 0) + FrameUtil.GetTile2x2Step2( + decFrame.G, decFrame.Width, x + x2 + x3 - 4, y + y2 + y3, dataG); + else if (x2 == 0 && y2 == 0 && y3 != 0) + FrameUtil.GetTile2x2Step2( + decFrame.G, decFrame.Width, x + x2 + x3, y + y2 + y3 - 4, dataG); + else + FrameUtil.GetTile2x2Step2( + decFrame.G, decFrame.Width, x + x2 + x3 - 1, y + y2 + y3, dataG); + + for (int j = 0; j < 4; j++) + intData[j] = data[j] - dataG[j]; + Dct.Dct4NoDiv(intData, dct); + Quantize4(dct, quantsG[i], deQuants[i]); + Dct.IDct4(deQuants[i], dataG, undct); + FrameUtil.SetTile2x2Step2(decFrame.G, decFrame.Width, x + x2 + x3, y + y2 + y3, + undct); + i++; + } + } + } + } + + var combDct2 = new int[64]; + for (int j = 0; j < 4; j++) + { + for (i = 0; i < 16; i++) + { + combDct2[j * 16 + i] = quantsG[i][j]; + } + } + + combDct2[0] -= lastGDC; + lastGDC = quantsG[0][0]; + + Vlc.EncodeDct(combDct2, bw); + + i = 0; + for (int y3 = 0; y3 < 8; y3 += 4) + { + for (int x3 = 0; x3 < 8; x3 += 4) + { + for (int y2 = 0; y2 < 2; y2++) + { + for (int x2 = 0; x2 < 2; x2++) + { + FrameUtil.GetTile2x2Step2(frame.R, frame.Width, x + x2 + x3, y + y2 + y3, data); + if ((x3 + x2) == 0 && (y3 + y2) == 0) + FrameUtil.GetTile2x2Step2( + decFrame.G, decFrame.Width, x + x2 + x3, y + y2 + y3, dataG); + else if (x2 == 0 && y2 != 0) + FrameUtil.GetTile2x2Step2( + decFrame.R, decFrame.Width, x + x3, y + y2 + y3 - 1, dataG); + else if (x2 == 0 && x3 != 0) + FrameUtil.GetTile2x2Step2( + decFrame.R, decFrame.Width, x + x2 + x3 - 4, y + y2 + y3, dataG); + else if (x2 == 0 && y2 == 0 && y3 != 0) + FrameUtil.GetTile2x2Step2( + decFrame.R, decFrame.Width, x + x2 + x3, y + y2 + y3 - 4, dataG); + else + FrameUtil.GetTile2x2Step2( + decFrame.R, decFrame.Width, x + x2 + x3 - 1, y + y2 + y3, dataG); + for (int j = 0; j < 4; j++) + intData[j] = data[j] - dataG[j]; + Dct.Dct4NoDiv(intData, dct); + Quantize4(dct, quantsR[i], deQuants[i]); + Dct.IDct4(deQuants[i], dataG, undct); + FrameUtil.SetTile2x2Step2(decFrame.R, decFrame.Width, x + x2 + x3, y + y2 + y3, + undct); + i++; + } + } + } + } + + for (int j = 0; j < 4; j++) + { + for (i = 0; i < 16; i++) + { + combDct2[j * 16 + i] = quantsR[i][j]; + } + } + + Vlc.EncodeDct(combDct2, bw); + + i = 0; + for (int y3 = 0; y3 < 8; y3 += 4) + { + for (int x3 = 0; x3 < 8; x3 += 4) + { + for (int y2 = 0; y2 < 2; y2++) + { + for (int x2 = 0; x2 < 2; x2++) + { + FrameUtil.GetTile2x2Step2(frame.B, frame.Width, x + x2 + x3, y + y2 + y3, data); + if ((x3 + x2) == 0 && (y3 + y2) == 0) + FrameUtil.GetTile2x2Step2( + decFrame.G, decFrame.Width, x + x2 + x3, y + y2 + y3, dataG); + else if (x2 == 0 && y2 != 0) + FrameUtil.GetTile2x2Step2( + decFrame.B, decFrame.Width, x + x3, y + y2 + y3 - 1, dataG); + else if (x2 == 0 && x3 != 0) + FrameUtil.GetTile2x2Step2( + decFrame.B, decFrame.Width, x + x2 + x3 - 4, y + y2 + y3, dataG); + else if (x2 == 0 && y2 == 0 && y3 != 0) + FrameUtil.GetTile2x2Step2( + decFrame.B, decFrame.Width, x + x2 + x3, y + y2 + y3 - 4, dataG); + else + FrameUtil.GetTile2x2Step2( + decFrame.B, decFrame.Width, x + x2 + x3 - 1, y + y2 + y3, dataG); + for (int j = 0; j < 4; j++) + intData[j] = data[j] - dataG[j]; + Dct.Dct4NoDiv(intData, dct); + Quantize4(dct, quantsB[i], deQuants[i]); + Dct.IDct4(deQuants[i], dataG, undct); + FrameUtil.SetTile2x2Step2(decFrame.B, decFrame.Width, x + x2 + x3, y + y2 + y3, + undct); + i++; + } + } + } + } + + for (int j = 0; j < 4; j++) + { + for (i = 0; i < 16; i++) + { + combDct2[j * 16 + i] = quantsB[i][j]; + } + } + + Vlc.EncodeDct(combDct2, bw); + } + } + + + bw.Flush(); + return (bw.ToArray(), decRefFrame); + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe bool AllZero(int[] src) + { + fixed (int* pSrc = src) + { + var a = Avx.LoadVector256(pSrc); + a = Avx2.Or(a, Avx.LoadVector256(pSrc + 1 * 8)); + a = Avx2.Or(a, Avx.LoadVector256(pSrc + 2 * 8)); + a = Avx2.Or(a, Avx.LoadVector256(pSrc + 3 * 8)); + a = Avx2.Or(a, Avx.LoadVector256(pSrc + 4 * 8)); + a = Avx2.Or(a, Avx.LoadVector256(pSrc + 5 * 8)); + a = Avx2.Or(a, Avx.LoadVector256(pSrc + 6 * 8)); + a = Avx2.Or(a, Avx.LoadVector256(pSrc + 7 * 8)); + return Avx.TestZ(a, Vector256.AllBitsSet); + } + } + + private class BlockConfig + { + public readonly int[][] DctsR = new int[16][]; + public readonly int[][] QuantsR = new int[16][]; + public readonly int[][] DeQuantsR = new int[16][]; + public readonly int[] CombDctR = new int[64]; + + public readonly int[][] DctsG = new int[16][]; + public readonly int[][] QuantsG = new int[16][]; + public readonly int[][] DeQuantsG = new int[16][]; + public readonly int[] CombDctG = new int[64]; + + public readonly int[][] DctsB = new int[16][]; + public readonly int[][] QuantsB = new int[16][]; + public readonly int[][] DeQuantsB = new int[16][]; + public readonly int[] CombDctB = new int[64]; + + public BlockConfig() + { + for (int i = 0; i < 16; i++) + { + DctsR[i] = new int[4]; + QuantsR[i] = new int[4]; + DeQuantsR[i] = new int[4]; + DctsG[i] = new int[4]; + QuantsG[i] = new int[4]; + DeQuantsG[i] = new int[4]; + DctsB[i] = new int[4]; + QuantsB[i] = new int[4]; + DeQuantsB[i] = new int[4]; + } + } + } + + private (byte[] resultData, RefFrame resultFrame, int iBlockCount) EncodePFrame(Rgb555Frame frame, + Rgb555Frame refFrame) + { + if (frame.Width != Width) + throw new Exception("Invalid frame width!"); + if (frame.Height != Height) + throw new Exception("Invalid frame height!"); + + int iBlockCount = 0; + + var decRefFrame = _framePool.AcquireFrame(); + var decFrame = decRefFrame.Frame; + var vectorBw = new BitWriter(); + var dctBw = new BitWriter(); + vectorBw.WriteBits(1, 1); //0 = I frame, 1 = P frame + // bw.WriteVarIntSigned(_q - _oldQ); + + var iBlockConfig = new BlockConfig(); + var pBlockConfig = new BlockConfig(); + + var vecBuf = new MotionVector[2][]; + vecBuf[0] = new MotionVector[Width / 8]; + vecBuf[1] = new MotionVector[Width / 8]; + int curVecBuf = 0; + + var lastVec = new MotionVector(0, 0); + + var predR = new byte[64]; + var predG = new byte[64]; + var predB = new byte[64]; + var fullDataR = new byte[64]; + var fullDataG = new byte[64]; + var fullDataB = new byte[64]; + var tmp = new byte[64]; + var data = new byte[4]; + var dataG = new byte[4]; + var intData = new int[4]; + var dct = new int[4]; + var undct = new byte[4]; + + // int lastGDC = 0; + for (int y = 0; y < Height; y += 8) + { + for (int x = 0; x < Width; x += 8) + { + int predX = 0; + int predY = 0; + + var mvecX = new List(); + var mvecY = new List(); + if (x != 0) + { + mvecX.Add(vecBuf[curVecBuf][(x >> 3) - 1].X); + mvecY.Add(vecBuf[curVecBuf][(x >> 3) - 1].Y); + } + + if (y != 0) + { + mvecX.Add(vecBuf[1 - curVecBuf][x >> 3].X); + mvecY.Add(vecBuf[1 - curVecBuf][x >> 3].Y); + if (x != Width - 8) + { + mvecX.Add(vecBuf[1 - curVecBuf][(x >> 3) + 1].X); + mvecY.Add(vecBuf[1 - curVecBuf][(x >> 3) + 1].Y); + } + } + + if (mvecX.Count != 0) + { + mvecX.Sort(); + mvecY.Sort(); + predX = mvecX[mvecX.Count / 2]; + predY = mvecY[mvecY.Count / 2]; + } + + lastVec.X = predX; + lastVec.Y = predY; + + FrameUtil.GetTile8(frame.R, frame.Width, x, y, fullDataR); + FrameUtil.GetTile8(frame.G, frame.Width, x, y, fullDataG); + FrameUtil.GetTile8(frame.B, frame.Width, x, y, fullDataB); + + // Rgb555Frame refFrame = _lastFrame; + + int bestScore; + var vec = MotionEstimation.FindMotionVector(fullDataR, fullDataG, fullDataB, refFrame, + new MotionVector(x << 1, y << 1), + new MotionVector(lastVec.X + (x << 1), lastVec.Y + (y << 1)), + out bestScore); + + FrameUtil.GetTileHalf8(refFrame.R, refFrame.Width, refFrame.Height, vec.X, vec.Y, predR); + FrameUtil.GetTileHalf8(refFrame.G, refFrame.Width, refFrame.Height, vec.X, vec.Y, predG); + FrameUtil.GetTileHalf8(refFrame.B, refFrame.Width, refFrame.Height, vec.X, vec.Y, predB); + + vec.X -= x << 1; + vec.Y -= y << 1; + + int iBitCount = 1; + var iTmpG = new byte[64]; + var iTmpR = new byte[64]; + var iTmpB = new byte[64]; + { + int i = 0; + for (int y3 = 0; y3 < 8; y3 += 4) + { + for (int x3 = 0; x3 < 8; x3 += 4) + { + for (int y2 = 0; y2 < 2; y2++) + { + for (int x2 = 0; x2 < 2; x2++) + { + FrameUtil.GetTile2x2Step2(frame.G, frame.Width, x + x2 + x3, y + y2 + y3, data); + if ((x3 + x2) == 0 && (y3 + y2) == 0) + Array.Clear(dataG, 0, 4); + else if (x2 == 0 && y2 != 0) + FrameUtil.GetTile2x2Step2(iTmpG, 8, x3, y2 + y3 - 1, dataG); + else if (x2 == 0 && x3 != 0) + FrameUtil.GetTile2x2Step2(iTmpG, 8, x2 + x3 - 4, y2 + y3, dataG); + else if (x2 == 0 && y2 == 0 && y3 != 0) + FrameUtil.GetTile2x2Step2(iTmpG, 8, x2 + x3, y2 + y3 - 4, dataG); + else + FrameUtil.GetTile2x2Step2(iTmpG, 8, x2 + x3 - 1, y2 + y3, dataG); + + for (int j = 0; j < 4; j++) + intData[j] = data[j] - dataG[j]; + Dct.Dct4NoDiv(intData, dct); + Quantize4(dct, iBlockConfig.QuantsG[i], iBlockConfig.DeQuantsG[i]); + Dct.IDct4(iBlockConfig.DeQuantsG[i], dataG, undct); + FrameUtil.SetTile2x2Step2(iTmpG, 8, x2 + x3, y2 + y3, undct); + i++; + } + } + } + } + + for (int j = 0; j < 4; j++) + { + for (i = 0; i < 16; i++) + { + iBlockConfig.CombDctG[j * 16 + i] = iBlockConfig.QuantsG[i][j]; + } + } + + // iBlockConfig.CombDctG[0] -= 512 * QTab4[0] >> 18;//lastGDC; + // lastGDC = quantsG[0][0]; + + iBitCount += Vlc.CalcDctBitCount(iBlockConfig.CombDctG); + // EncodeDCT(combDct2, 1, bw); + + i = 0; + for (int y3 = 0; y3 < 8; y3 += 4) + { + for (int x3 = 0; x3 < 8; x3 += 4) + { + for (int y2 = 0; y2 < 2; y2++) + { + for (int x2 = 0; x2 < 2; x2++) + { + FrameUtil.GetTile2x2Step2(frame.R, frame.Width, x + x2 + x3, y + y2 + y3, data); + if ((x3 + x2) == 0 && (y3 + y2) == 0) + FrameUtil.GetTile2x2Step2(iTmpG, 8, x2 + x3, y2 + y3, dataG); + else if (x2 == 0 && y2 != 0) + FrameUtil.GetTile2x2Step2(iTmpR, 8, x3, y2 + y3 - 1, dataG); + else if (x2 == 0 && x3 != 0) + FrameUtil.GetTile2x2Step2(iTmpR, 8, x2 + x3 - 4, y2 + y3, dataG); + else if (x2 == 0 && y2 == 0 && y3 != 0) + FrameUtil.GetTile2x2Step2(iTmpR, 8, x2 + x3, y2 + y3 - 4, dataG); + else + FrameUtil.GetTile2x2Step2(iTmpR, 8, x2 + x3 - 1, y2 + y3, dataG); + for (int j = 0; j < 4; j++) + intData[j] = data[j] - dataG[j]; + Dct.Dct4NoDiv(intData, dct); + Quantize4(dct, iBlockConfig.QuantsR[i], iBlockConfig.DeQuantsR[i]); + Dct.IDct4(iBlockConfig.DeQuantsR[i], dataG, undct); + FrameUtil.SetTile2x2Step2(iTmpR, 8, x2 + x3, y2 + y3, undct); + i++; + } + } + } + } + + for (int j = 0; j < 4; j++) + { + for (i = 0; i < 16; i++) + { + iBlockConfig.CombDctR[j * 16 + i] = iBlockConfig.QuantsR[i][j]; + } + } + + iBitCount += Vlc.CalcDctBitCount(iBlockConfig.CombDctR); + // EncodeDCT(combDct2, 1, bw); + + i = 0; + for (int y3 = 0; y3 < 8; y3 += 4) + { + for (int x3 = 0; x3 < 8; x3 += 4) + { + for (int y2 = 0; y2 < 2; y2++) + { + for (int x2 = 0; x2 < 2; x2++) + { + FrameUtil.GetTile2x2Step2(frame.B, frame.Width, x + x2 + x3, y + y2 + y3, data); + if ((x3 + x2) == 0 && (y3 + y2) == 0) + FrameUtil.GetTile2x2Step2(iTmpG, 8, x2 + x3, y2 + y3, dataG); + else if (x2 == 0 && y2 != 0) + FrameUtil.GetTile2x2Step2(iTmpB, 8, x3, y2 + y3 - 1, dataG); + else if (x2 == 0 && x3 != 0) + FrameUtil.GetTile2x2Step2(iTmpB, 8, x2 + x3 - 4, y2 + y3, dataG); + else if (x2 == 0 && y2 == 0 && y3 != 0) + FrameUtil.GetTile2x2Step2(iTmpB, 8, x2 + x3, y2 + y3 - 4, dataG); + else + FrameUtil.GetTile2x2Step2(iTmpB, 8, x2 + x3 - 1, y2 + y3, dataG); + for (int j = 0; j < 4; j++) + intData[j] = data[j] - dataG[j]; + Dct.Dct4NoDiv(intData, dct); + Quantize4(dct, iBlockConfig.QuantsB[i], iBlockConfig.DeQuantsB[i]); + Dct.IDct4(iBlockConfig.DeQuantsB[i], dataG, undct); + FrameUtil.SetTile2x2Step2(iTmpB, 8, x2 + x3, y2 + y3, undct); + i++; + } + } + } + } + + for (int j = 0; j < 4; j++) + { + for (i = 0; i < 16; i++) + { + iBlockConfig.CombDctB[j * 16 + i] = iBlockConfig.QuantsB[i][j]; + } + } + + iBitCount += Vlc.CalcDctBitCount(iBlockConfig.CombDctB); + // EncodeDCT(combDct2, 1, bw); + } + + int pBitCount = 0; + var pTmpG = new byte[64]; + var pTmpR = new byte[64]; + var pTmpB = new byte[64]; + { + pBitCount++; + if (vec != lastVec) + { + pBitCount += BitWriter.GetSignedVarIntBitCount(vec.X - lastVec.X); + pBitCount += BitWriter.GetSignedVarIntBitCount(vec.Y - lastVec.Y); + } + + int i = 0; + for (int y3 = 0; y3 < 8; y3 += 4) + { + for (int x3 = 0; x3 < 8; x3 += 4) + { + for (int y2 = 0; y2 < 2; y2++) + { + for (int x2 = 0; x2 < 2; x2++) + { + FrameUtil.GetTile2x2Step2(frame.G, frame.Width, x + x2 + x3, y + y2 + y3, data); + int r = 0; + for (int ry = y3 + y2; ry < y3 + y2 + 4; ry += 2) + for (int rx = x3 + x2; rx < x3 + x2 + 4; rx += 2) + dataG[r++] = predG[ry * 8 + rx]; + for (int j = 0; j < 4; j++) + intData[j] = data[j] - dataG[j]; + Dct.Dct4NoDiv(intData, pBlockConfig.DctsG[i]); + i++; + } + } + } + } + + pBitCount += QuantizeComb4PRD(pBlockConfig.DctsG, pBlockConfig.CombDctG, + pBlockConfig.DeQuantsG); + i = 0; + for (int y3 = 0; y3 < 8; y3 += 4) + { + for (int x3 = 0; x3 < 8; x3 += 4) + { + for (int y2 = 0; y2 < 2; y2++) + { + for (int x2 = 0; x2 < 2; x2++) + { + int r = 0; + for (int ry = y3 + y2; ry < y3 + y2 + 4; ry += 2) + for (int rx = x3 + x2; rx < x3 + x2 + 4; rx += 2) + dataG[r++] = predG[ry * 8 + rx]; + Dct.IDct4(pBlockConfig.DeQuantsG[i], dataG, undct); + FrameUtil.SetTile2x2Step2(pTmpG, 8, x2 + x3, y2 + y3, undct); + i++; + } + } + } + } + + // for (int j = 0; j < 4; j++) + // { + // for (i = 0; i < 16; i++) + // { + // pBlockConfig.CombDctG[j * 16 + i] = pBlockConfig.QuantsG[i][j]; + // } + // } + // + // pBitCount++; + // if (pBlockConfig.CombDctG.Any(a => a != 0)) + // pBitCount += CalcDCTBitCount(pBlockConfig.CombDctG, 1); + + // pBitCount += CalcBitsQuantsP2(pBlockConfig.QuantsG); + + //pBitCount += CalcBitsQuantsP(pBlockConfig.QuantsG); + + i = 0; + for (int y3 = 0; y3 < 8; y3 += 4) + { + for (int x3 = 0; x3 < 8; x3 += 4) + { + for (int y2 = 0; y2 < 2; y2++) + { + for (int x2 = 0; x2 < 2; x2++) + { + FrameUtil.GetTile2x2Step2(frame.R, frame.Width, x + x2 + x3, y + y2 + y3, data); + int r = 0; + for (int ry = y3 + y2; ry < y3 + y2 + 4; ry += 2) + for (int rx = x3 + x2; rx < x3 + x2 + 4; rx += 2) + dataG[r++] = predR[ry * 8 + rx]; + for (int j = 0; j < 4; j++) + intData[j] = data[j] - dataG[j]; + Dct.Dct4NoDiv(intData, pBlockConfig.DctsR[i]); + // Quantize4P(pBlockConfig.DctsR[i], pBlockConfig.QuantsR[i], pBlockConfig.DeQuantsR[i]); + // FrameUtil.SetTile(pTmpR, x2 + x3, y2 + y3, 2, 2, 2, + // DCTUtil.IDCT4(pBlockConfig.DeQuantsR[i], dataG)); + i++; + } + } + } + } + + pBitCount += QuantizeComb4PRD(pBlockConfig.DctsR, pBlockConfig.CombDctR, + pBlockConfig.DeQuantsR); + + i = 0; + for (int y3 = 0; y3 < 8; y3 += 4) + { + for (int x3 = 0; x3 < 8; x3 += 4) + { + for (int y2 = 0; y2 < 2; y2++) + { + for (int x2 = 0; x2 < 2; x2++) + { + int r = 0; + for (int ry = y3 + y2; ry < y3 + y2 + 4; ry += 2) + for (int rx = x3 + x2; rx < x3 + x2 + 4; rx += 2) + dataG[r++] = predR[ry * 8 + rx]; + Dct.IDct4(pBlockConfig.DeQuantsR[i], dataG, undct); + FrameUtil.SetTile2x2Step2(pTmpR, 8, x2 + x3, y2 + y3, undct); + i++; + } + } + } + } + + // for (int j = 0; j < 4; j++) + // { + // for (i = 0; i < 16; i++) + // { + // pBlockConfig.CombDctR[j * 16 + i] = pBlockConfig.QuantsR[i][j]; + // } + // } + // + // pBitCount++; + // if (pBlockConfig.CombDctR.Any(a => a != 0)) + // pBitCount += CalcDCTBitCount(pBlockConfig.CombDctR, 1); + + // pBitCount += CalcBitsQuantsP2(pBlockConfig.QuantsR); + // pBitCount += CalcDCTBitCount(pBlockConfig.CombDctR, 1);//pBitCount += CalcBitsQuantsP(pBlockConfig.QuantsR); + + i = 0; + for (int y3 = 0; y3 < 8; y3 += 4) + { + for (int x3 = 0; x3 < 8; x3 += 4) + { + for (int y2 = 0; y2 < 2; y2++) + { + for (int x2 = 0; x2 < 2; x2++) + { + FrameUtil.GetTile2x2Step2(frame.B, frame.Width, x + x2 + x3, y + y2 + y3, data); + int r = 0; + for (int ry = y3 + y2; ry < y3 + y2 + 4; ry += 2) + for (int rx = x3 + x2; rx < x3 + x2 + 4; rx += 2) + dataG[r++] = predB[ry * 8 + rx]; + for (int j = 0; j < 4; j++) + intData[j] = data[j] - dataG[j]; + Dct.Dct4NoDiv(intData, pBlockConfig.DctsB[i]); + // Quantize4P(dct, pBlockConfig.QuantsB[i], pBlockConfig.DeQuantsB[i]); + // FrameUtil.SetTile(pTmpB, x2 + x3, y2 + y3, 2, 2, 2, + // DCTUtil.IDCT4(pBlockConfig.DeQuantsB[i], dataG)); + i++; + } + } + } + } + + pBitCount += QuantizeComb4PRD(pBlockConfig.DctsB, pBlockConfig.CombDctB, + pBlockConfig.DeQuantsB); + + i = 0; + for (int y3 = 0; y3 < 8; y3 += 4) + { + for (int x3 = 0; x3 < 8; x3 += 4) + { + for (int y2 = 0; y2 < 2; y2++) + { + for (int x2 = 0; x2 < 2; x2++) + { + int r = 0; + for (int ry = y3 + y2; ry < y3 + y2 + 4; ry += 2) + for (int rx = x3 + x2; rx < x3 + x2 + 4; rx += 2) + dataG[r++] = predB[ry * 8 + rx]; + Dct.IDct4(pBlockConfig.DeQuantsB[i], dataG, undct); + FrameUtil.SetTile2x2Step2(pTmpB, 8, x2 + x3, y2 + y3, undct); + i++; + } + } + } + } + + // for (int j = 0; j < 4; j++) + // { + // for (i = 0; i < 16; i++) + // { + // pBlockConfig.CombDctB[j * 16 + i] = pBlockConfig.QuantsB[i][j]; + // } + // } + // + // pBitCount++; + // if (pBlockConfig.CombDctB.Any(a => a != 0)) + // pBitCount += CalcDCTBitCount(pBlockConfig.CombDctB, 1); + + // pBitCount += CalcBitsQuantsP2(pBlockConfig.QuantsB); + // pBitCount += CalcDCTBitCount(pBlockConfig.CombDctB, 1);//pBitCount += CalcBitsQuantsP(pBlockConfig.QuantsB); + } + + FrameUtil.GetTile8(frame.G, frame.Width, x, y, tmp); + int diffI = FrameUtil.Sad64(iTmpG, tmp); + int diffP = FrameUtil.Sad64(pTmpG, tmp); + FrameUtil.GetTile8(frame.R, frame.Width, x, y, tmp); + diffI += FrameUtil.Sad64(iTmpR, tmp); + diffP += FrameUtil.Sad64(pTmpR, tmp); + FrameUtil.GetTile8(frame.B, frame.Width, x, y, tmp); + diffI += FrameUtil.Sad64(iTmpB, tmp); + diffP += FrameUtil.Sad64(pTmpB, tmp); + + if (iBitCount + diffI * _lambda < pBitCount + diffP * _lambda) + { + // dctCoefCount += iBlockConfig.CombDctG.Count(a => a != 0); + // dctCoefCount += iBlockConfig.CombDctR.Count(a => a != 0); + // dctCoefCount += iBlockConfig.CombDctB.Count(a => a != 0); + dctBw.WriteBits(1, 1); //I-block + vectorBw.WriteBits(1, 1); + Vlc.EncodeDct(iBlockConfig.CombDctG, dctBw); + Vlc.EncodeDct(iBlockConfig.CombDctR, dctBw); + Vlc.EncodeDct(iBlockConfig.CombDctB, dctBw); + + FrameUtil.SetTile8(decFrame.R, decFrame.Width, x, y, iTmpR); + FrameUtil.SetTile8(decFrame.G, decFrame.Width, x, y, iTmpG); + FrameUtil.SetTile8(decFrame.B, decFrame.Width, x, y, iTmpB); + + vecBuf[curVecBuf][x >> 3] = lastVec; + + iBlockCount++; + } + else + { + dctBw.WriteBits(0, 1); //P-block + if (vec == lastVec) + { + vectorBw.WriteBits(1, 1); + } + else + { + vectorBw.WriteBits(0, 1); + vectorBw.WriteSignedVarInt(vec.X - lastVec.X); + vectorBw.WriteSignedVarInt(vec.Y - lastVec.Y); + } + + // EncodeQuantsP(bw, pBlockConfig.QuantsG); + // EncodeQuantsP(bw, pBlockConfig.QuantsR); + // EncodeQuantsP(bw, pBlockConfig.QuantsB); + // EncodeQuantsP2(bw, pBlockConfig.QuantsG); + // EncodeQuantsP2(bw, pBlockConfig.QuantsR); + // EncodeQuantsP2(bw, pBlockConfig.QuantsB); + + if (!AllZero(pBlockConfig.CombDctG)) + { + dctBw.WriteBits(1, 1); + Vlc.EncodeDct(pBlockConfig.CombDctG, dctBw); + } + else + dctBw.WriteBits(0, 1); + + if (!AllZero(pBlockConfig.CombDctR)) + { + dctBw.WriteBits(1, 1); + Vlc.EncodeDct(pBlockConfig.CombDctR, dctBw); + } + else + dctBw.WriteBits(0, 1); + + if (!AllZero(pBlockConfig.CombDctB)) + { + dctBw.WriteBits(1, 1); + Vlc.EncodeDct(pBlockConfig.CombDctB, dctBw); + } + else + dctBw.WriteBits(0, 1); + + FrameUtil.SetTile8(decFrame.R, decFrame.Width, x, y, pTmpR); + FrameUtil.SetTile8(decFrame.G, decFrame.Width, x, y, pTmpG); + FrameUtil.SetTile8(decFrame.B, decFrame.Width, x, y, pTmpB); + + vecBuf[curVecBuf][x >> 3] = vec; + // dctCoefCount += pBlockConfig.CombDctG.Count(a => a != 0); + // dctCoefCount += pBlockConfig.CombDctR.Count(a => a != 0); + // dctCoefCount += pBlockConfig.CombDctB.Count(a => a != 0); + } + } + + curVecBuf = 1 - curVecBuf; + } + + // Console.WriteLine(dctCoefCount); + + var vecData = vectorBw.ToArray(); + var dctData = dctBw.ToArray(); + + var finalData = new byte[vecData.Length + dctData.Length]; + vecData.CopyTo(finalData, 0); + dctData.CopyTo(finalData, vecData.Length); + + return (finalData, decRefFrame, iBlockCount); + } + + // private (byte[] resultData, RefFrame resultFrame, int iBlockCount) EncodeBFrame(Rgb555Frame frame, + // Rgb555Frame backRefFrame, Rgb555Frame forwardRefFrame) + // { + // if (frame.Width != Width) + // throw new Exception("Invalid frame width!"); + // if (frame.Height != Height) + // throw new Exception("Invalid frame height!"); + // + // // var refFrame = new Rgb555Frame(frame.Width, frame.Height); + // // GxAverage(backRefFrame.R, forwardRefFrame.R, refFrame.R); + // // GxAverage(backRefFrame.G, forwardRefFrame.G, refFrame.G); + // // GxAverage(backRefFrame.B, forwardRefFrame.B, refFrame.B); + // + // int iBlockCount = 0; + // + // var decRefFrame = _framePool.AcquireFrame(); + // var decFrame = decRefFrame.Frame; + // var vectorBw = new BitWriter(); + // var dctBw = new BitWriter(); + // vectorBw.WriteBits(1, 1); //0 = I frame, 1 = P frame + // // bw.WriteVarIntSigned(_q - _oldQ); + // + // var iBlockConfig = new BlockConfig(); + // var pBlockConfig = new BlockConfig(); + // + // var vecBuf = new MotionVector[2][]; + // vecBuf[0] = new MotionVector[Width / 8]; + // vecBuf[1] = new MotionVector[Width / 8]; + // int curVecBuf = 0; + // + // var lastVec = new MotionVector(0, 0); + // + // var predR = new byte[64]; + // var predG = new byte[64]; + // var predB = new byte[64]; + // var fullDataR = new byte[64]; + // var fullDataG = new byte[64]; + // var fullDataB = new byte[64]; + // var tmp = new byte[64]; + // var data = new byte[4]; + // var dataG = new byte[4]; + // var intData = new int[4]; + // var dct = new int[4]; + // var undct = new byte[4]; + // + // // int lastGDC = 0; + // for (int y = 0; y < Height; y += 8) + // { + // for (int x = 0; x < Width; x += 8) + // { + // int predX = 0; + // int predY = 0; + // + // var mvecX = new List(); + // var mvecY = new List(); + // if (x != 0) + // { + // mvecX.Add(vecBuf[curVecBuf][(x >> 3) - 1].X); + // mvecY.Add(vecBuf[curVecBuf][(x >> 3) - 1].Y); + // } + // + // if (y != 0) + // { + // mvecX.Add(vecBuf[1 - curVecBuf][x >> 3].X); + // mvecY.Add(vecBuf[1 - curVecBuf][x >> 3].Y); + // if (x != Width - 8) + // { + // mvecX.Add(vecBuf[1 - curVecBuf][(x >> 3) + 1].X); + // mvecY.Add(vecBuf[1 - curVecBuf][(x >> 3) + 1].Y); + // } + // } + // + // if (mvecX.Count != 0) + // { + // mvecX.Sort(); + // mvecY.Sort(); + // predX = mvecX[mvecX.Count / 2]; + // predY = mvecY[mvecY.Count / 2]; + // } + // + // lastVec.X = predX; + // lastVec.Y = predY; + // + // FrameUtil.GetTile8(frame.R, frame.Width, x, y, fullDataR); + // FrameUtil.GetTile8(frame.G, frame.Width, x, y, fullDataG); + // FrameUtil.GetTile8(frame.B, frame.Width, x, y, fullDataB); + // + // // Rgb555Frame refFrame = _lastFrame; + // + // // int bestScore; + // // Rgb555Frame bestFrame = refFrame; + // // var vec = MotionEstimationUtil.FindMotionVector(fullDataR, fullDataG, fullDataB, + // // refFrame, 8, 8, + // // new (x << 1, y << 1), + // // new (lastVec.X + (x << 1), lastVec.Y + (y << 1)), 7, out bestScore); + // MotionVector vec; + // MotionVector vec2; + // int bestScore; + // Rgb555Frame bestFrame; + // + // { + // int backScore; + // var vecBack = MotionEstimation.FindMotionVector(fullDataR, fullDataG, fullDataB, + // backRefFrame, + // new(x << 1, y << 1), + // new(lastVec.X + (x << 1), lastVec.Y + (y << 1)), out backScore); + // + // // if (backScore < bestScore) + // { + // vec = vecBack; + // bestScore = backScore; + // bestFrame = backRefFrame; + // } + // + // int forwardScore; + // var vecForward = MotionEstimation.FindMotionVector(fullDataR, fullDataG, fullDataB, + // forwardRefFrame, + // new(x << 1, y << 1), + // new(lastVec.X + (x << 1), lastVec.Y + (y << 1)), out forwardScore); + // + // if (forwardScore < bestScore) + // { + // vec = vecForward; + // bestScore = forwardScore; + // bestFrame = forwardRefFrame; + // } + // + // var predBackR = new byte[64]; + // var predBackG = new byte[64]; + // var predBackB = new byte[64]; + // + // var predForwardR = new byte[64]; + // var predForwardG = new byte[64]; + // var predForwardB = new byte[64]; + // + // FrameUtil.GetTileHalf8(backRefFrame.R, backRefFrame.Width, backRefFrame.Height, vecBack.X, + // vecBack.Y, predBackR); + // FrameUtil.GetTileHalf8(backRefFrame.G, backRefFrame.Width, backRefFrame.Height, vecBack.X, + // vecBack.Y, predBackG); + // FrameUtil.GetTileHalf8(backRefFrame.B, backRefFrame.Width, backRefFrame.Height, vecBack.X, + // vecBack.Y, predBackB); + // + // FrameUtil.GetTileHalf8(forwardRefFrame.R, forwardRefFrame.Width, forwardRefFrame.Height, + // vecForward.X, vecForward.Y, predForwardR); + // FrameUtil.GetTileHalf8(forwardRefFrame.G, forwardRefFrame.Width, forwardRefFrame.Height, + // vecForward.X, vecForward.Y, predForwardG); + // FrameUtil.GetTileHalf8(forwardRefFrame.B, forwardRefFrame.Width, forwardRefFrame.Height, + // vecForward.X, vecForward.Y, predForwardB); + // + // GxAverage(predBackR, predForwardR, predR); + // GxAverage(predBackG, predForwardG, predG); + // GxAverage(predBackB, predForwardB, predB); + // + // int mixScore = FrameUtil.Sad64(fullDataR, predR); + // mixScore += FrameUtil.Sad64(fullDataG, predG); + // mixScore += FrameUtil.Sad64(fullDataB, predB); + // + // if (mixScore < bestScore) + // { + // vec = vecBack; + // vec2 = vecForward; + // bestFrame = null; + // } + // } + // + // if (bestFrame != null) + // { + // FrameUtil.GetTileHalf8(bestFrame.R, bestFrame.Width, bestFrame.Height, vec.X, vec.Y, predR); + // FrameUtil.GetTileHalf8(bestFrame.G, bestFrame.Width, bestFrame.Height, vec.X, vec.Y, predG); + // FrameUtil.GetTileHalf8(bestFrame.B, bestFrame.Width, bestFrame.Height, vec.X, vec.Y, predB); + // } + // + // vec.X -= x << 1; + // vec.Y -= y << 1; + // + // int iBitCount = 1; + // var iTmpG = new byte[64]; + // var iTmpR = new byte[64]; + // var iTmpB = new byte[64]; + // { + // int i = 0; + // for (int y3 = 0; y3 < 8; y3 += 4) + // { + // for (int x3 = 0; x3 < 8; x3 += 4) + // { + // for (int y2 = 0; y2 < 2; y2++) + // { + // for (int x2 = 0; x2 < 2; x2++) + // { + // FrameUtil.GetTile2x2Step2(frame.G, frame.Width, x + x2 + x3, y + y2 + y3, data); + // if ((x3 + x2) == 0 && (y3 + y2) == 0) + // Array.Clear(dataG, 0, 4); + // else if (x2 == 0 && y2 != 0) + // FrameUtil.GetTile2x2Step2(iTmpG, 8, x3, y2 + y3 - 1, dataG); + // else if (x2 == 0 && x3 != 0) + // FrameUtil.GetTile2x2Step2(iTmpG, 8, x2 + x3 - 4, y2 + y3, dataG); + // else if (x2 == 0 && y2 == 0 && y3 != 0) + // FrameUtil.GetTile2x2Step2(iTmpG, 8, x2 + x3, y2 + y3 - 4, dataG); + // else + // FrameUtil.GetTile2x2Step2(iTmpG, 8, x2 + x3 - 1, y2 + y3, dataG); + // + // for (int j = 0; j < 4; j++) + // intData[j] = data[j] - dataG[j]; + // Dct.Dct4NoDiv(intData, dct); + // Quantize4(dct, iBlockConfig.QuantsG[i], iBlockConfig.DeQuantsG[i]); + // Dct.IDct4(iBlockConfig.DeQuantsG[i], dataG, undct); + // FrameUtil.SetTile2x2Step2(iTmpG, 8, x2 + x3, y2 + y3, undct); + // i++; + // } + // } + // } + // } + // + // for (int j = 0; j < 4; j++) + // { + // for (i = 0; i < 16; i++) + // { + // iBlockConfig.CombDctG[j * 16 + i] = iBlockConfig.QuantsG[i][j]; + // } + // } + // + // // iBlockConfig.CombDctG[0] -= 512 * QTab4[0] >> 18;//lastGDC; + // // lastGDC = quantsG[0][0]; + // + // iBitCount += Vlc.CalcDctBitCount(iBlockConfig.CombDctG); + // // EncodeDCT(combDct2, 1, bw); + // + // i = 0; + // for (int y3 = 0; y3 < 8; y3 += 4) + // { + // for (int x3 = 0; x3 < 8; x3 += 4) + // { + // for (int y2 = 0; y2 < 2; y2++) + // { + // for (int x2 = 0; x2 < 2; x2++) + // { + // FrameUtil.GetTile2x2Step2(frame.R, frame.Width, x + x2 + x3, y + y2 + y3, data); + // if ((x3 + x2) == 0 && (y3 + y2) == 0) + // FrameUtil.GetTile2x2Step2(iTmpG, 8, x2 + x3, y2 + y3, dataG); + // else if (x2 == 0 && y2 != 0) + // FrameUtil.GetTile2x2Step2(iTmpR, 8, x3, y2 + y3 - 1, dataG); + // else if (x2 == 0 && x3 != 0) + // FrameUtil.GetTile2x2Step2(iTmpR, 8, x2 + x3 - 4, y2 + y3, dataG); + // else if (x2 == 0 && y2 == 0 && y3 != 0) + // FrameUtil.GetTile2x2Step2(iTmpR, 8, x2 + x3, y2 + y3 - 4, dataG); + // else + // FrameUtil.GetTile2x2Step2(iTmpR, 8, x2 + x3 - 1, y2 + y3, dataG); + // for (int j = 0; j < 4; j++) + // intData[j] = data[j] - dataG[j]; + // Dct.Dct4NoDiv(intData, dct); + // Quantize4(dct, iBlockConfig.QuantsR[i], iBlockConfig.DeQuantsR[i]); + // Dct.IDct4(iBlockConfig.DeQuantsR[i], dataG, undct); + // FrameUtil.SetTile2x2Step2(iTmpR, 8, x2 + x3, y2 + y3, undct); + // i++; + // } + // } + // } + // } + // + // for (int j = 0; j < 4; j++) + // { + // for (i = 0; i < 16; i++) + // { + // iBlockConfig.CombDctR[j * 16 + i] = iBlockConfig.QuantsR[i][j]; + // } + // } + // + // iBitCount += Vlc.CalcDctBitCount(iBlockConfig.CombDctR); + // // EncodeDCT(combDct2, 1, bw); + // + // i = 0; + // for (int y3 = 0; y3 < 8; y3 += 4) + // { + // for (int x3 = 0; x3 < 8; x3 += 4) + // { + // for (int y2 = 0; y2 < 2; y2++) + // { + // for (int x2 = 0; x2 < 2; x2++) + // { + // FrameUtil.GetTile2x2Step2(frame.B, frame.Width, x + x2 + x3, y + y2 + y3, data); + // if ((x3 + x2) == 0 && (y3 + y2) == 0) + // FrameUtil.GetTile2x2Step2(iTmpG, 8, x2 + x3, y2 + y3, dataG); + // else if (x2 == 0 && y2 != 0) + // FrameUtil.GetTile2x2Step2(iTmpB, 8, x3, y2 + y3 - 1, dataG); + // else if (x2 == 0 && x3 != 0) + // FrameUtil.GetTile2x2Step2(iTmpB, 8, x2 + x3 - 4, y2 + y3, dataG); + // else if (x2 == 0 && y2 == 0 && y3 != 0) + // FrameUtil.GetTile2x2Step2(iTmpB, 8, x2 + x3, y2 + y3 - 4, dataG); + // else + // FrameUtil.GetTile2x2Step2(iTmpB, 8, x2 + x3 - 1, y2 + y3, dataG); + // for (int j = 0; j < 4; j++) + // intData[j] = data[j] - dataG[j]; + // Dct.Dct4NoDiv(intData, dct); + // Quantize4(dct, iBlockConfig.QuantsB[i], iBlockConfig.DeQuantsB[i]); + // Dct.IDct4(iBlockConfig.DeQuantsB[i], dataG, undct); + // FrameUtil.SetTile2x2Step2(iTmpB, 8, x2 + x3, y2 + y3, undct); + // i++; + // } + // } + // } + // } + // + // for (int j = 0; j < 4; j++) + // { + // for (i = 0; i < 16; i++) + // { + // iBlockConfig.CombDctB[j * 16 + i] = iBlockConfig.QuantsB[i][j]; + // } + // } + // + // iBitCount += Vlc.CalcDctBitCount(iBlockConfig.CombDctB); + // // EncodeDCT(combDct2, 1, bw); + // } + // + // int pBitCount = 0; + // var pTmpG = new byte[64]; + // var pTmpR = new byte[64]; + // var pTmpB = new byte[64]; + // { + // pBitCount++; + // if (vec != lastVec) + // { + // pBitCount += BitWriter.GetSignedVarIntBitCount(vec.X - lastVec.X); + // pBitCount += BitWriter.GetSignedVarIntBitCount(vec.Y - lastVec.Y); + // } + // + // int i = 0; + // for (int y3 = 0; y3 < 8; y3 += 4) + // { + // for (int x3 = 0; x3 < 8; x3 += 4) + // { + // for (int y2 = 0; y2 < 2; y2++) + // { + // for (int x2 = 0; x2 < 2; x2++) + // { + // FrameUtil.GetTile2x2Step2(frame.G, frame.Width, x + x2 + x3, y + y2 + y3, data); + // int r = 0; + // for (int ry = y3 + y2; ry < y3 + y2 + 4; ry += 2) + // for (int rx = x3 + x2; rx < x3 + x2 + 4; rx += 2) + // dataG[r++] = predG[ry * 8 + rx]; + // for (int j = 0; j < 4; j++) + // intData[j] = data[j] - dataG[j]; + // Dct.Dct4NoDiv(intData, pBlockConfig.DctsG[i]); + // i++; + // } + // } + // } + // } + // + // pBitCount += QuantizeComb4PRD(pBlockConfig.DctsG, pBlockConfig.CombDctG, + // pBlockConfig.DeQuantsG); + // i = 0; + // for (int y3 = 0; y3 < 8; y3 += 4) + // { + // for (int x3 = 0; x3 < 8; x3 += 4) + // { + // for (int y2 = 0; y2 < 2; y2++) + // { + // for (int x2 = 0; x2 < 2; x2++) + // { + // int r = 0; + // for (int ry = y3 + y2; ry < y3 + y2 + 4; ry += 2) + // for (int rx = x3 + x2; rx < x3 + x2 + 4; rx += 2) + // dataG[r++] = predG[ry * 8 + rx]; + // Dct.IDct4(pBlockConfig.DeQuantsG[i], dataG, undct); + // FrameUtil.SetTile2x2Step2(pTmpG, 8, x2 + x3, y2 + y3, undct); + // i++; + // } + // } + // } + // } + // + // // for (int j = 0; j < 4; j++) + // // { + // // for (i = 0; i < 16; i++) + // // { + // // pBlockConfig.CombDctG[j * 16 + i] = pBlockConfig.QuantsG[i][j]; + // // } + // // } + // // + // // pBitCount++; + // // if (pBlockConfig.CombDctG.Any(a => a != 0)) + // // pBitCount += CalcDCTBitCount(pBlockConfig.CombDctG, 1); + // + // // pBitCount += CalcBitsQuantsP2(pBlockConfig.QuantsG); + // + // //pBitCount += CalcBitsQuantsP(pBlockConfig.QuantsG); + // + // i = 0; + // for (int y3 = 0; y3 < 8; y3 += 4) + // { + // for (int x3 = 0; x3 < 8; x3 += 4) + // { + // for (int y2 = 0; y2 < 2; y2++) + // { + // for (int x2 = 0; x2 < 2; x2++) + // { + // FrameUtil.GetTile2x2Step2(frame.R, frame.Width, x + x2 + x3, y + y2 + y3, data); + // int r = 0; + // for (int ry = y3 + y2; ry < y3 + y2 + 4; ry += 2) + // for (int rx = x3 + x2; rx < x3 + x2 + 4; rx += 2) + // dataG[r++] = predR[ry * 8 + rx]; + // for (int j = 0; j < 4; j++) + // intData[j] = data[j] - dataG[j]; + // Dct.Dct4NoDiv(intData, pBlockConfig.DctsR[i]); + // // Quantize4P(pBlockConfig.DctsR[i], pBlockConfig.QuantsR[i], pBlockConfig.DeQuantsR[i]); + // // FrameUtil.SetTile(pTmpR, x2 + x3, y2 + y3, 2, 2, 2, + // // DCTUtil.IDCT4(pBlockConfig.DeQuantsR[i], dataG)); + // i++; + // } + // } + // } + // } + // + // pBitCount += QuantizeComb4PRD(pBlockConfig.DctsR, pBlockConfig.CombDctR, + // pBlockConfig.DeQuantsR); + // + // i = 0; + // for (int y3 = 0; y3 < 8; y3 += 4) + // { + // for (int x3 = 0; x3 < 8; x3 += 4) + // { + // for (int y2 = 0; y2 < 2; y2++) + // { + // for (int x2 = 0; x2 < 2; x2++) + // { + // int r = 0; + // for (int ry = y3 + y2; ry < y3 + y2 + 4; ry += 2) + // for (int rx = x3 + x2; rx < x3 + x2 + 4; rx += 2) + // dataG[r++] = predR[ry * 8 + rx]; + // Dct.IDct4(pBlockConfig.DeQuantsR[i], dataG, undct); + // FrameUtil.SetTile2x2Step2(pTmpR, 8, x2 + x3, y2 + y3, undct); + // i++; + // } + // } + // } + // } + // + // // for (int j = 0; j < 4; j++) + // // { + // // for (i = 0; i < 16; i++) + // // { + // // pBlockConfig.CombDctR[j * 16 + i] = pBlockConfig.QuantsR[i][j]; + // // } + // // } + // // + // // pBitCount++; + // // if (pBlockConfig.CombDctR.Any(a => a != 0)) + // // pBitCount += CalcDCTBitCount(pBlockConfig.CombDctR, 1); + // + // // pBitCount += CalcBitsQuantsP2(pBlockConfig.QuantsR); + // // pBitCount += CalcDCTBitCount(pBlockConfig.CombDctR, 1);//pBitCount += CalcBitsQuantsP(pBlockConfig.QuantsR); + // + // i = 0; + // for (int y3 = 0; y3 < 8; y3 += 4) + // { + // for (int x3 = 0; x3 < 8; x3 += 4) + // { + // for (int y2 = 0; y2 < 2; y2++) + // { + // for (int x2 = 0; x2 < 2; x2++) + // { + // FrameUtil.GetTile2x2Step2(frame.B, frame.Width, x + x2 + x3, y + y2 + y3, data); + // int r = 0; + // for (int ry = y3 + y2; ry < y3 + y2 + 4; ry += 2) + // for (int rx = x3 + x2; rx < x3 + x2 + 4; rx += 2) + // dataG[r++] = predB[ry * 8 + rx]; + // for (int j = 0; j < 4; j++) + // intData[j] = data[j] - dataG[j]; + // Dct.Dct4NoDiv(intData, pBlockConfig.DctsB[i]); + // // Quantize4P(dct, pBlockConfig.QuantsB[i], pBlockConfig.DeQuantsB[i]); + // // FrameUtil.SetTile(pTmpB, x2 + x3, y2 + y3, 2, 2, 2, + // // DCTUtil.IDCT4(pBlockConfig.DeQuantsB[i], dataG)); + // i++; + // } + // } + // } + // } + // + // pBitCount += QuantizeComb4PRD(pBlockConfig.DctsB, pBlockConfig.CombDctB, + // pBlockConfig.DeQuantsB); + // + // i = 0; + // for (int y3 = 0; y3 < 8; y3 += 4) + // { + // for (int x3 = 0; x3 < 8; x3 += 4) + // { + // for (int y2 = 0; y2 < 2; y2++) + // { + // for (int x2 = 0; x2 < 2; x2++) + // { + // int r = 0; + // for (int ry = y3 + y2; ry < y3 + y2 + 4; ry += 2) + // for (int rx = x3 + x2; rx < x3 + x2 + 4; rx += 2) + // dataG[r++] = predB[ry * 8 + rx]; + // Dct.IDct4(pBlockConfig.DeQuantsB[i], dataG, undct); + // FrameUtil.SetTile2x2Step2(pTmpB, 8, x2 + x3, y2 + y3, undct); + // i++; + // } + // } + // } + // } + // + // // for (int j = 0; j < 4; j++) + // // { + // // for (i = 0; i < 16; i++) + // // { + // // pBlockConfig.CombDctB[j * 16 + i] = pBlockConfig.QuantsB[i][j]; + // // } + // // } + // // + // // pBitCount++; + // // if (pBlockConfig.CombDctB.Any(a => a != 0)) + // // pBitCount += CalcDCTBitCount(pBlockConfig.CombDctB, 1); + // + // // pBitCount += CalcBitsQuantsP2(pBlockConfig.QuantsB); + // // pBitCount += CalcDCTBitCount(pBlockConfig.CombDctB, 1);//pBitCount += CalcBitsQuantsP(pBlockConfig.QuantsB); + // } + // + // FrameUtil.GetTile8(frame.G, frame.Width, x, y, tmp); + // int diffI = FrameUtil.Sad64(iTmpG, tmp); + // int diffP = FrameUtil.Sad64(pTmpG, tmp); + // FrameUtil.GetTile8(frame.R, frame.Width, x, y, tmp); + // diffI += FrameUtil.Sad64(iTmpR, tmp); + // diffP += FrameUtil.Sad64(pTmpR, tmp); + // FrameUtil.GetTile8(frame.B, frame.Width, x, y, tmp); + // diffI += FrameUtil.Sad64(iTmpB, tmp); + // diffP += FrameUtil.Sad64(pTmpB, tmp); + // + // if (iBitCount + diffI * _lambda < pBitCount + diffP * _lambda) + // { + // // dctCoefCount += iBlockConfig.CombDctG.Count(a => a != 0); + // // dctCoefCount += iBlockConfig.CombDctR.Count(a => a != 0); + // // dctCoefCount += iBlockConfig.CombDctB.Count(a => a != 0); + // dctBw.WriteBits(1, 1); //I-block + // vectorBw.WriteBits(1, 1); + // Vlc.EncodeDct(iBlockConfig.CombDctG, dctBw); + // Vlc.EncodeDct(iBlockConfig.CombDctR, dctBw); + // Vlc.EncodeDct(iBlockConfig.CombDctB, dctBw); + // + // FrameUtil.SetTile8(decFrame.R, decFrame.Width, x, y, iTmpR); + // FrameUtil.SetTile8(decFrame.G, decFrame.Width, x, y, iTmpG); + // FrameUtil.SetTile8(decFrame.B, decFrame.Width, x, y, iTmpB); + // + // vecBuf[curVecBuf][x >> 3] = lastVec; + // + // iBlockCount++; + // } + // else + // { + // dctBw.WriteBits(0, 1); //P-block + // if (vec == lastVec) + // { + // vectorBw.WriteBits(1, 1); + // } + // else + // { + // vectorBw.WriteBits(0, 1); + // vectorBw.WriteSignedVarInt(vec.X - lastVec.X); + // vectorBw.WriteSignedVarInt(vec.Y - lastVec.Y); + // } + // + // // EncodeQuantsP(bw, pBlockConfig.QuantsG); + // // EncodeQuantsP(bw, pBlockConfig.QuantsR); + // // EncodeQuantsP(bw, pBlockConfig.QuantsB); + // // EncodeQuantsP2(bw, pBlockConfig.QuantsG); + // // EncodeQuantsP2(bw, pBlockConfig.QuantsR); + // // EncodeQuantsP2(bw, pBlockConfig.QuantsB); + // + // if (!AllZero(pBlockConfig.CombDctG)) + // { + // dctBw.WriteBits(1, 1); + // Vlc.EncodeDct(pBlockConfig.CombDctG, dctBw); + // } + // else + // dctBw.WriteBits(0, 1); + // + // if (!AllZero(pBlockConfig.CombDctR)) + // { + // dctBw.WriteBits(1, 1); + // Vlc.EncodeDct(pBlockConfig.CombDctR, dctBw); + // } + // else + // dctBw.WriteBits(0, 1); + // + // if (!AllZero(pBlockConfig.CombDctB)) + // { + // dctBw.WriteBits(1, 1); + // Vlc.EncodeDct(pBlockConfig.CombDctB, dctBw); + // } + // else + // dctBw.WriteBits(0, 1); + // + // FrameUtil.SetTile8(decFrame.R, decFrame.Width, x, y, pTmpR); + // FrameUtil.SetTile8(decFrame.G, decFrame.Width, x, y, pTmpG); + // FrameUtil.SetTile8(decFrame.B, decFrame.Width, x, y, pTmpB); + // + // vecBuf[curVecBuf][x >> 3] = vec; + // // dctCoefCount += pBlockConfig.CombDctG.Count(a => a != 0); + // // dctCoefCount += pBlockConfig.CombDctR.Count(a => a != 0); + // // dctCoefCount += pBlockConfig.CombDctB.Count(a => a != 0); + // } + // } + // + // curVecBuf = 1 - curVecBuf; + // } + // + // // Console.WriteLine(dctCoefCount); + // + // var vecData = vectorBw.ToArray(); + // var dctData = dctBw.ToArray(); + // + // var finalData = new byte[vecData.Length + dctData.Length]; + // vecData.CopyTo(finalData, 0); + // dctData.CopyTo(finalData, vecData.Length); + // + // return (finalData, decRefFrame, iBlockCount); + // } + } +} \ No newline at end of file diff --git a/Gericom.FastVideoDS/Frames/FramePool.cs b/Gericom.FastVideoDS/Frames/FramePool.cs new file mode 100644 index 0000000..ac93b81 --- /dev/null +++ b/Gericom.FastVideoDS/Frames/FramePool.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; + +namespace Gericom.FastVideoDS.Frames +{ + public class FramePool + { + private readonly Queue _pool = new(); + + public readonly int Width; + public readonly int Height; + + public FramePool(int width, int height) + { + Width = width; + Height = height; + } + + public RefFrame AcquireFrame() + { + if (!_pool.TryDequeue(out var frame)) + frame = new Rgb555Frame(Width, Height); + + return new RefFrame(this, frame); + } + + public void ReleaseFrame(RefFrame frame) + { + _pool.Enqueue(frame.Frame); + } + } +} \ No newline at end of file diff --git a/Gericom.FastVideoDS/Frames/RefFrame.cs b/Gericom.FastVideoDS/Frames/RefFrame.cs new file mode 100644 index 0000000..ee59264 --- /dev/null +++ b/Gericom.FastVideoDS/Frames/RefFrame.cs @@ -0,0 +1,47 @@ +using System; + +namespace Gericom.FastVideoDS.Frames +{ + public sealed class RefFrame : IDisposable + { + private readonly FramePool _pool; + + public int RefCount { get; private set; } = 1; + + public readonly Rgb555Frame Frame; + + public RefFrame(FramePool pool, Rgb555Frame frame) + { + _pool = pool; + Frame = frame; + } + + public void Ref() + { + if (RefCount == 0) + throw new Exception(); + + RefCount++; + } + + public void Unref() + { + if (RefCount == 0) + throw new Exception(); + + if (--RefCount == 0) + _pool.ReleaseFrame(this); + } + + public void Dispose() + { + if (RefCount == 0) + return; + + Unref(); + + if (RefCount != 0) + throw new Exception(); + } + } +} \ No newline at end of file diff --git a/Gericom.FastVideoDS/Frames/Rgb555Frame.cs b/Gericom.FastVideoDS/Frames/Rgb555Frame.cs new file mode 100644 index 0000000..c96a1fd --- /dev/null +++ b/Gericom.FastVideoDS/Frames/Rgb555Frame.cs @@ -0,0 +1,186 @@ +using System; +using System.Drawing; + +namespace Gericom.FastVideoDS.Frames +{ + public class Rgb555Frame + { + public readonly int Width; + public readonly int Height; + + public readonly byte[] R; + public readonly byte[] G; + public readonly byte[] B; + + public Rgb555Frame(int width, int height) + { + if (width <= 0) + throw new ArgumentOutOfRangeException(nameof(width), width, "Width should be > 0"); + if (height <= 0) + throw new ArgumentOutOfRangeException(nameof(height), height, "Height should be > 0"); + Width = width; + Height = height; + R = new byte[height * width]; + G = new byte[height * width]; + B = new byte[height * width]; + } + + // public unsafe Bitmap ToBitmap() + // { + // var b = new Bitmap(Width, Height, PixelFormat.Format32bppArgb); + // var d = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.WriteOnly, + // PixelFormat.Format32bppArgb); + // for (int y = 0; y < b.Height; y++) + // { + // for (int x = 0; x < b.Width; x++) + // { + // // int pr = R[y, x] << 3; // * 255 / 31; + // // int pg = G[y, x] << 3; // * 255 / 31; + // // int pb = B[y, x] << 3; // * 255 / 31; + // + // int pr = (R[y * Width + x] >> 3) * 255 / 31; + // int pg = (G[y * Width + x] >> 3) * 255 / 31; + // int pb = (B[y * Width + x] >> 3) * 255 / 31; + // + // if (pr < 0) pr = 0; + // else if (pr > 255) pr = 255; + // if (pg < 0) pg = 0; + // else if (pg > 255) pg = 255; + // if (pb < 0) pb = 0; + // else if (pb > 255) pb = 255; + // + // *(int*)(((byte*)d.Scan0) + y * d.Stride + x * 4) = Color.FromArgb(pr, pg, pb).ToArgb(); + // } + // } + // + // b.UnlockBits(d); + // return b; + // } + + private static readonly int[,] DitherMatrix = + { + { 0, 12, 3, 15 }, + { 8, 4, 11, 7 }, + { 2, 14, 1, 13 }, + { 10, 6, 9, 5 } + }; + + // public static unsafe Rgb555Frame FromBitmap(Bitmap b) + // { + // var frame = new Rgb555Frame(b.Width, b.Height); + // var d = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, + // PixelFormat.Format32bppArgb); + // + // for (int y = 0; y < b.Height; y++) + // { + // for (int x = 0; x < b.Width; x++) + // { + // var c = Color.FromArgb(*(int*)(((byte*)d.Scan0) + y * d.Stride + x * 4)); + // int bias = (DitherMatrix[y & 3, x & 3] >> 1) - 4; + // + // int pr = ((c.R + bias) >> 3); // * 255 / 31; + // int pg = ((c.G + bias) >> 3); // * 255 / 31; + // int pb = ((c.B + bias) >> 3); // * 255 / 31; + // + // if (pr < 0) pr = 0; + // else if (pr > 31) pr = 31; + // if (pg < 0) pg = 0; + // else if (pg > 31) pg = 31; + // if (pb < 0) pb = 0; + // else if (pb > 31) pb = 31; + // + // frame.R[y * frame.Width + x] = (byte)(pr << 3); //((pr << 3) | (pr >> 2)); + // frame.G[y * frame.Width + x] = (byte)(pg << 3); //((pg << 3) | (pg >> 2)); + // frame.B[y * frame.Width + x] = (byte)(pb << 3); //((pb << 3) | (pb >> 2)); + // } + // } + // + // b.UnlockBits(d); + // return frame; + // } + + //Based on http://www.thetenthplanet.de/archives/5367 + private static double ToLinear(double value) => Math.Pow(value, 2.2); + + private static readonly double[] ToLinear8 = new double[256]; + private static readonly double[] ToLinear5 = new double[32]; + + static Rgb555Frame() + { + for (int i = 0; i < 256; i++) + ToLinear8[i] = ToLinear(i / 255.0); + + for (int i = 0; i < 32; i++) + ToLinear5[i] = ToLinear(i / 31.0); + } + + private static int Dither(int color, double noise) + { + int c0 = color * 31 / 255; + int c1 = Math.Clamp(c0 + 1, 0, 31); + double discr = ToLinear5[c0] * (1 - noise) + ToLinear5[c1] * noise; + return discr < ToLinear8[color] ? c1 : c0; + } + + public static unsafe Rgb555Frame FromRgba32(byte* src, int width, int height, int stride) + { + var frame = new Rgb555Frame(width, height); + frame.FromRgba32(src, stride); + return frame; + } + + public unsafe void FromRgba32(byte* src, int stride) + { + for (int y = 0; y < Height; y++) + { + for (int x = 0; x < Width; x++) + { + var c = Color.FromArgb(*(int*)(src + y * stride + x * 4)); + + int bias = DitherMatrix[y & 3, x & 3]; + + int pr = Dither(c.R, bias / 16.0); + int pg = Dither(c.G, bias / 16.0); + int pb = Dither(c.B, bias / 16.0); + + R[y * Width + x] = (byte)(pr << 3); + G[y * Width + x] = (byte)(pg << 3); + B[y * Width + x] = (byte)(pb << 3); + } + } + } + + // public static unsafe RGB555Frame FromBitmap555(Bitmap b) + // { + // var yuvFrame = new RGB555Frame(b.Width, b.Height); + // var d = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), ImageLockMode.ReadOnly, + // PixelFormat.Format32bppArgb); + // for (int y = 0; y < b.Height; y++) + // { + // for (int x = 0; x < b.Width; x++) + // { + // var c = Color.FromArgb(*(int*)(((byte*)d.Scan0) + y * d.Stride + x * 4)); + // int bias = (DitherMatrix[y & 3, x & 3] >> 1) - 4; + // + // int pr = ((c.R + bias) >> 3);// * 255 / 31; + // int pg = ((c.G + bias) >> 3);// * 255 / 31; + // int pb = ((c.B + bias) >> 3);// * 255 / 31; + // + // if (pr < 0) pr = 0; + // else if (pr > 31) pr = 31; + // if (pg < 0) pg = 0; + // else if (pg > 31) pg = 31; + // if (pb < 0) pb = 0; + // else if (pb > 31) pb = 31; + // + // yuvFrame.R[y, x] = (byte) pr;//((pr << 3) | (pr >> 2)); + // yuvFrame.G[y, x] = (byte) pg;//((pg << 3) | (pg >> 2)); + // yuvFrame.B[y, x] = (byte) pb;//((pb << 3) | (pb >> 2)); + // } + // } + // + // b.UnlockBits(d); + // return yuvFrame; + // } + } +} \ No newline at end of file diff --git a/Gericom.FastVideoDS/Gericom.FastVideoDS.csproj b/Gericom.FastVideoDS/Gericom.FastVideoDS.csproj new file mode 100644 index 0000000..05f917d --- /dev/null +++ b/Gericom.FastVideoDS/Gericom.FastVideoDS.csproj @@ -0,0 +1,15 @@ + + + + net6.0 + + + + true + + + + true + + + diff --git a/Gericom.FastVideoDS/MotionEstimation.cs b/Gericom.FastVideoDS/MotionEstimation.cs new file mode 100644 index 0000000..1dcacb2 --- /dev/null +++ b/Gericom.FastVideoDS/MotionEstimation.cs @@ -0,0 +1,157 @@ +using System; +using System.Runtime.CompilerServices; +using Gericom.FastVideoDS.Bitstream; +using Gericom.FastVideoDS.Frames; +using Gericom.FastVideoDS.Utils; + +namespace Gericom.FastVideoDS +{ + public static class MotionEstimation + { + private static readonly MotionVector[] LdspDirections = + { + new(-2, 0), + new(-1, -1), + new(0, -2), + new(1, -1), + new(2, 0), + new(1, 1), + new(0, 2), + new(-1, 1) + }; + + private static readonly MotionVector[] SdspDirections = + { + new(-1, 0), + new(0, -1), + new(1, 0), + new(0, 1), + }; + + public static MotionVector FindMotionVector(byte[] targetR, byte[] targetG, byte[] targetB, Rgb555Frame src, + MotionVector center, MotionVector cheap, out int bestScore) + { + const int lambda = 4; + var block = new byte[64]; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + int getDistortion(MotionVector vec) + { + if (center.Y < (src.Height >> 1) * 2 && vec.Y > (src.Height - 8) * 2 || + center.Y >= (src.Height >> 1) * 2 && vec.Y < 0) + return 999999; + + FrameUtil.GetTileHalf8(src.R, src.Width, src.Height, vec.X, vec.Y, block); + int score = FrameUtil.Sad64(targetR, block); + FrameUtil.GetTileHalf8(src.G, src.Width, src.Height, vec.X, vec.Y, block); + score += FrameUtil.Sad64(targetG, block); + FrameUtil.GetTileHalf8(src.B, src.Width, src.Height, vec.X, vec.Y, block); + score += FrameUtil.Sad64(targetB, block); + + return score; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + int getBitCount(MotionVector vec) + { + return vec == cheap ? 1 : + 1 + BitWriter.GetSignedVarIntBitCount(vec.X - cheap.X) + + BitWriter.GetSignedVarIntBitCount(vec.Y - cheap.Y); + } + + // var vectors = new HashSet(); + bestScore = getDistortion(center); + int bestBitCount = getBitCount(center); + int bestRdScore = bestBitCount * lambda + bestScore; + var bestRdVec = center; + var bestVec = center; + + int score = getDistortion(cheap); + int bitCount = getBitCount(cheap); + int rdScore = bitCount * lambda + score; + if (score < bestScore || score == bestScore && bitCount < bestBitCount) + { + bestScore = score; + bestBitCount = bitCount; + bestVec = cheap; + } + + if (rdScore < bestRdScore || Math.Abs(rdScore - bestRdScore) < 0.001f && score < bestScore) + { + bestRdScore = rdScore; + bestRdVec = cheap; + } + + var searchCenter = cheap; + + int count = 0; + int bestIdx; + do + { + bestIdx = -1; + for (int i = 0; i < LdspDirections.Length; i++) + { + var vec = searchCenter + LdspDirections[i]; + // if(vectors.Contains(vec)) + // continue; + // vectors.Add(vec); + score = getDistortion(vec); + bitCount = getBitCount(vec); + rdScore = bitCount * lambda + score; + if (score < bestScore || score == bestScore && bitCount < bestBitCount) + { + bestScore = score; + bestBitCount = bitCount; + bestVec = vec; + bestIdx = i; + } + + if (rdScore < bestRdScore || Math.Abs(rdScore - bestRdScore) < 0.001f && score < bestScore) + { + bestRdScore = rdScore; + bestRdVec = vec; + } + + count++; + } + + searchCenter = bestVec; + } while (bestIdx != -1 && count < 128); //32); + + count = 0; + do + { + bestIdx = -1; + for (int i = 0; i < SdspDirections.Length; i++) + { + var vec = searchCenter + SdspDirections[i]; + // if (vectors.Contains(vec)) + // continue; + // vectors.Add(vec); + score = getDistortion(vec); + bitCount = getBitCount(vec); + rdScore = bitCount * lambda + score; + if (score < bestScore || score == bestScore && bitCount < bestBitCount) + { + bestScore = score; + bestBitCount = bitCount; + bestVec = vec; + bestIdx = i; + } + + if (rdScore < bestRdScore || Math.Abs(rdScore - bestRdScore) < 0.001f && score < bestScore) + { + bestRdScore = rdScore; + bestRdVec = vec; + } + + count++; + } + + searchCenter = bestVec; + } while (bestIdx != -1 && count < 128); //32); + + return bestRdVec; + } + } +} \ No newline at end of file diff --git a/Gericom.FastVideoDS/MotionVector.cs b/Gericom.FastVideoDS/MotionVector.cs new file mode 100644 index 0000000..d8b780d --- /dev/null +++ b/Gericom.FastVideoDS/MotionVector.cs @@ -0,0 +1,44 @@ +using System.Runtime.CompilerServices; + +namespace Gericom.FastVideoDS +{ + public struct MotionVector + { + public int X; + public int Y; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MotionVector(int x, int y) + { + X = x; + Y = y; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static MotionVector operator +(MotionVector a, MotionVector b) + => new(a.X + b.X, a.Y + b.Y); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static MotionVector operator *(MotionVector a, int mul) + => new(a.X * mul, a.Y * mul); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(MotionVector left, MotionVector right) + => left.Equals(right); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(MotionVector left, MotionVector right) + => !left.Equals(right); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(MotionVector other) + => X == other.X && Y == other.Y; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object obj) + => obj is MotionVector other && Equals(other); + + public override int GetHashCode() + => unchecked(X * 397 ^ Y); + } +} \ No newline at end of file diff --git a/Gericom.FastVideoDS/Utils/FrameUtil.cs b/Gericom.FastVideoDS/Utils/FrameUtil.cs new file mode 100644 index 0000000..c0f6f65 --- /dev/null +++ b/Gericom.FastVideoDS/Utils/FrameUtil.cs @@ -0,0 +1,595 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace Gericom.FastVideoDS.Utils +{ + public static class FrameUtil + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void GetTile8(byte[] data, int stride, int srcX, int srcY, byte[] result) + { + fixed (byte* dst = &result[0], src = &data[srcY * stride + srcX]) + { + ((ulong*)dst)[0] = *(ulong*)(src); + ((ulong*)dst)[1] = *(ulong*)(src + 1 * stride); + ((ulong*)dst)[2] = *(ulong*)(src + 2 * stride); + ((ulong*)dst)[3] = *(ulong*)(src + 3 * stride); + ((ulong*)dst)[4] = *(ulong*)(src + 4 * stride); + ((ulong*)dst)[5] = *(ulong*)(src + 5 * stride); + ((ulong*)dst)[6] = *(ulong*)(src + 6 * stride); + ((ulong*)dst)[7] = *(ulong*)(src + 7 * stride); + } + } + + public static byte[] GetTile(byte[] data, int stride, int srcX, int srcY, int width, int height) + { + var result = new byte[height * width]; + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + result[y * width + x] = data[(y + srcY) * stride + (x + srcX)]; + } + } + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void GetTile2x2Step2(byte[] data, int stride, int srcX, int srcY, byte[] dst) + { + dst[0] = data[(srcY) * stride + srcX]; + dst[1] = data[(srcY) * stride + srcX + 2]; + dst[2] = data[(srcY + 2) * stride + srcX]; + dst[3] = data[(srcY + 2) * stride + srcX + 2]; + } + + public static byte[] GetTile(byte[] data, int stride, int srcX, int srcY, int width, int height, int step) + { + var result = new byte[height * width]; + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + result[y * width + x] = data[(y * step + srcY) * stride + x * step + srcX]; + } + } + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + public static unsafe void GetTileHalf8(byte[] data, int width, int height, int srcX, int srcY, byte[] result) + { + if (((srcX | srcY) & 1) == 0) + { + if (srcX >> 1 >= 0 && (srcX >> 1) + 7 < width && + srcY >> 1 >= 0 && (srcY >> 1) + 7 < height) + { + fixed (byte* dst = &result[0], src = &data[(srcY >> 1) * width + (srcX >> 1)]) + { + ((ulong*)dst)[0] = *(ulong*)(src); + ((ulong*)dst)[1] = *(ulong*)(src + 1 * width); + ((ulong*)dst)[2] = *(ulong*)(src + 2 * width); + ((ulong*)dst)[3] = *(ulong*)(src + 3 * width); + ((ulong*)dst)[4] = *(ulong*)(src + 4 * width); + ((ulong*)dst)[5] = *(ulong*)(src + 5 * width); + ((ulong*)dst)[6] = *(ulong*)(src + 6 * width); + ((ulong*)dst)[7] = *(ulong*)(src + 7 * width); + } + } + else + { + for (int y = 0; y < 8; y++) + { + int y1 = Math.Clamp(y + (srcY >> 1), 0, height - 1); + for (int x = 0; x < 8; x++) + { + int x1 = Math.Clamp(x + (srcX >> 1), 0, width - 1); + result[y * 8 + x] = data[y1 * width + x1]; + } + } + } + } + else if ((srcY & 1) == 0) + { + if (srcX >> 1 >= 0 && (srcX >> 1) + 8 < width && + srcY >> 1 >= 0 && (srcY >> 1) + 7 < height) + { + fixed (byte* dst = &result[0], src = &data[(srcY >> 1) * width + (srcX >> 1)]) + { + var bit0 = Vector256.Create((short)(1 << 2)); + for (int y = 0; y < 8; y++) + { + ulong row = *(ulong*)(src + y * width); + ulong row2 = (row >> 8) | ((ulong)src[y * width + 8] << 56); + + var a = Avx2.ConvertToVector256Int16(Vector128.Create(row, row2).AsByte()); + var isZero = Avx2.CompareEqual(a, Vector256.Zero); + a = Avx2.Add(a, bit0); + a = Avx2.AndNot(isZero, a); + var b = Sse2.Add(a.GetLower(), a.GetUpper()); + b = Sse2.ShiftRightLogical(b, 4); + b = Sse2.ShiftLeftLogical(b, 3); + *(ulong*)(dst + y * 8) = Sse2.PackUnsignedSaturate(b, Vector128.Zero).AsUInt64() + .ToScalar(); + } + } + + // for (int y = 0; y < 8; y++) + // { + // for (int x = 0; x < 8; x++) + // { + // int a = data[(y + (srcY >> 1)) * width + x + (srcX >> 1)] >> 3 << 1; + // if (a != 0) + // a++; + // int b = data[(y + (srcY >> 1)) * width + x + (srcX >> 1) + 1] >> 3 << 1; + // if (b != 0) + // b++; + // result[y * 8 + x] = (byte) ((a * 16 + b * 16) >> 6 << 3); + // } + // } + } + else + { + for (int y = 0; y < 8; y++) + { + int y1 = Math.Clamp(y + (srcY >> 1), 0, height - 1); + for (int x = 0; x < 8; x++) + { + int x1 = Math.Clamp(x + (srcX >> 1), 0, width - 1); + int x2 = Math.Clamp(x + (srcX >> 1) + 1, 0, width - 1); + int a = data[y1 * width + x1] >> 3 << 1; + if (a != 0) + a++; + int b = data[y1 * width + x2] >> 3 << 1; + if (b != 0) + b++; + result[y * 8 + x] = (byte)((a * 16 + b * 16) >> 6 << 3); + } + } + } + } + else if ((srcX & 1) == 0) + { + if (srcX >> 1 >= 0 && (srcX >> 1) + 7 < width && + srcY >> 1 >= 0 && (srcY >> 1) + 8 < height) + { + fixed (byte* dst = &result[0], src = &data[(srcY >> 1) * width + (srcX >> 1)]) + { + var bit0 = Vector256.Create((short)(1 << 2)); + var ac = Avx2.ConvertToVector256Int16( + Vector128.Create(*(ulong*)(src + 0 * width), *(ulong*)(src + 2 * width)).AsByte()); + var isZero = Avx2.CompareEqual(ac, Vector256.Zero); + ac = Avx2.Add(ac, bit0); + ac = Avx2.AndNot(isZero, ac); + + var bd = Avx2.ConvertToVector256Int16( + Vector128.Create(*(ulong*)(src + 1 * width), *(ulong*)(src + 3 * width)).AsByte()); + isZero = Avx2.CompareEqual(bd, Vector256.Zero); + bd = Avx2.Add(bd, bit0); + bd = Avx2.AndNot(isZero, bd); + + + var aBcD = Avx2.Add(ac, bd); + aBcD = Avx2.ShiftRightLogical(aBcD, 4); + aBcD = Avx2.ShiftLeftLogical(aBcD, 3); + + + var eg = Avx2.ConvertToVector256Int16( + Vector128.Create(*(ulong*)(src + 4 * width), *(ulong*)(src + 6 * width)).AsByte()); + isZero = Avx2.CompareEqual(eg, Vector256.Zero); + eg = Avx2.Add(eg, bit0); + eg = Avx2.AndNot(isZero, eg); + + var ce = Vector256.Create(ac.GetUpper(), eg.GetLower()); + var bCdE = Avx2.Add(bd, ce); + bCdE = Avx2.ShiftRightLogical(bCdE, 4); + bCdE = Avx2.ShiftLeftLogical(bCdE, 3); + Avx.Store(dst, Avx2.PackUnsignedSaturate(aBcD, bCdE)); + + var fh = Avx2.ConvertToVector256Int16( + Vector128.Create(*(ulong*)(src + 5 * width), *(ulong*)(src + 7 * width)).AsByte()); + isZero = Avx2.CompareEqual(fh, Vector256.Zero); + fh = Avx2.Add(fh, bit0); + fh = Avx2.AndNot(isZero, fh); + + var eFgH = Avx2.Add(eg, fh); + eFgH = Avx2.ShiftRightLogical(eFgH, 4); + eFgH = Avx2.ShiftLeftLogical(eFgH, 3); + + var last = Sse41.ConvertToVector128Int16(src + 8 * width); + var isZeroLast = Sse2.CompareEqual(last, Vector128.Zero); + last = Sse2.Add(last, Vector128.Create((short)(1 << 2))); + last = Sse2.AndNot(isZeroLast, last); + + var gi = Vector256.Create(eg.GetUpper(), last); + var fGhI = Avx2.Add(fh, gi); + fGhI = Avx2.ShiftRightLogical(fGhI, 4); + fGhI = Avx2.ShiftLeftLogical(fGhI, 3); + Avx.Store(dst + 4 * 8, Avx2.PackUnsignedSaturate(eFgH, fGhI)); + } + + // for (int y = 0; y < 8; y++) + // { + // for (int x = 0; x < 8; x++) + // { + // int a = data[(y + (srcY >> 1)) * width + x + (srcX >> 1)] >> 3 << 1; + // if (a != 0) + // a++; + // int b = data[(y + (srcY >> 1) + 1) * width + x + (srcX >> 1)] >> 3 << 1; + // if (b != 0) + // b++; + // if(result[y * 8 + x] != (byte)((a * 16 + b * 16) >> 6 << 3)) + // { + // + // } + // result[y * 8 + x] = (byte) ((a * 16 + b * 16) >> 6 << 3); + // } + // } + } + else + { + for (int y = 0; y < 8; y++) + { + int y1 = Math.Clamp(y + (srcY >> 1), 0, height - 1); + int y2 = Math.Clamp(y + (srcY >> 1) + 1, 0, height - 1); + for (int x = 0; x < 8; x++) + { + int x1 = Math.Clamp(x + (srcX >> 1), 0, width - 1); + int a = data[y1 * width + x1] >> 3 << 1; + if (a != 0) + a++; + int b = data[y2 * width + x1] >> 3 << 1; + if (b != 0) + b++; + result[y * 8 + x] = (byte)((a * 16 + b * 16) >> 6 << 3); + } + } + } + } + else + { + if (srcX >> 1 >= 0 && (srcX >> 1) + 8 < width && + srcY >> 1 >= 0 && (srcY >> 1) + 8 < height) + { + fixed (byte* dst = &result[0], src = &data[(srcY >> 1) * width + (srcX >> 1)]) + { + var bit0 = Vector256.Create((short)(1 << 2)); + for (int y = 0; y < 8; y++) + { + var a = Avx2.ConvertToVector256Int16( + Vector128.Create(*(ulong*)(src + y * width), *(ulong*)(src + (y + 1) * width + 1)) + .AsByte()); + var isZero = Avx2.CompareEqual(a, Vector256.Zero); + a = Avx2.Add(a, bit0); + a = Avx2.AndNot(isZero, a); + var b = Sse2.Add(a.GetLower(), a.GetUpper()); + b = Sse2.ShiftRightLogical(b, 4); + b = Sse2.ShiftLeftLogical(b, 3); + *(ulong*)(dst + y * 8) = Sse2.PackUnsignedSaturate(b, Vector128.Zero).AsUInt64() + .ToScalar(); + } + } + + // for (int y = 0; y < 8; y++) + // { + // for (int x = 0; x < 8; x++) + // { + // int a = data[(y + (srcY >> 1)) * width + x + (srcX >> 1)] >> 3 << 1; + // if (a != 0) + // a++; + // int b = data[(y + (srcY >> 1) + 1) * width + x + (srcX >> 1) + 1] >> 3 << 1; + // if (b != 0) + // b++; + // result[y * 8 + x] = (byte) ((a * 16 + b * 16) >> 6 << 3); + // } + // } + } + else + { + for (int y = 0; y < 8; y++) + { + int y1 = Math.Clamp(y + (srcY >> 1), 0, height - 1); + int y2 = Math.Clamp(y + (srcY >> 1) + 1, 0, height - 1); + for (int x = 0; x < 8; x++) + { + int x1 = Math.Clamp(x + (srcX >> 1), 0, width - 1); + int x2 = Math.Clamp(x + (srcX >> 1) + 1, 0, width - 1); + int a = data[y1 * width + x1] >> 3 << 1; + if (a != 0) + a++; + int b = data[y2 * width + x2] >> 3 << 1; + if (b != 0) + b++; + result[y * 8 + x] = (byte)((a * 16 + b * 16) >> 6 << 3); + } + } + } + } + } + + // public static unsafe byte[] GetTileHalf(byte[,] data, int srcX, int srcY, int width, int height) + // { + // var result = new byte[height * width]; + // if (((srcX | srcY) & 1) == 0) + // { + // if (srcX >> 1 >= 0 && (srcX >> 1) + width - 1 < data.GetLength(1) && + // srcY >> 1 >= 0 && (srcY >> 1) + height - 1 < data.GetLength(0)) + // { + // fixed (byte* dst = &result[0]) + // { + // for (int y = 0; y < height; y++) + // { + // fixed (byte* src = &data[(srcY >> 1) + y, srcX >> 1]) + // { + // Buffer.MemoryCopy(src, dst + y * width, width, width); + // } + // } + // } + // } + // else + // { + // for (int y = 0; y < height; y++) + // { + // for (int x = 0; x < width; x++) + // { + // int x1 = MathUtil.Clamp(x + (srcX >> 1), 0, data.GetLength(1) - 1); + // int y1 = MathUtil.Clamp(y + (srcY >> 1), 0, data.GetLength(0) - 1); + // result[y * width + x] = data[y1, x1]; + // } + // } + // } + // } + // else if ((srcY & 1) == 0) + // { + // for (int y = 0; y < height; y++) + // { + // for (int x = 0; x < width; x++) + // { + // int x1 = MathUtil.Clamp(x + (srcX >> 1), 0, data.GetLength(1) - 1); + // int x2 = MathUtil.Clamp(x + (srcX >> 1) + 1, 0, data.GetLength(1) - 1); + // int y1 = MathUtil.Clamp(y + (srcY >> 1), 0, data.GetLength(0) - 1); + // int a = data[y1, x1] >> 3 << 1; + // if (a != 0) + // a++; + // int b = data[y1, x2] >> 3 << 1; + // if (b != 0) + // b++; + // result[y * width + x] = (byte) ((a * 16 + b * 16) >> 6 << 3); + // //(byte) (((data[y1, x1] + data[y1, x2] + 8) >> 1) & 0xF8); + // } + // } + // } + // else if ((srcX & 1) == 0) + // { + // for (int y = 0; y < height; y++) + // { + // for (int x = 0; x < width; x++) + // { + // int x1 = MathUtil.Clamp(x + (srcX >> 1), 0, data.GetLength(1) - 1); + // int y1 = MathUtil.Clamp(y + (srcY >> 1), 0, data.GetLength(0) - 1); + // int y2 = MathUtil.Clamp(y + (srcY >> 1) + 1, 0, data.GetLength(0) - 1); + // int a = data[y1, x1] >> 3 << 1; + // if (a != 0) + // a++; + // int b = data[y2, x1] >> 3 << 1; + // if (b != 0) + // b++; + // result[y * width + x] = (byte) ((a * 16 + b * 16) >> 6 << 3); + // // result[y * width + x] = (byte) (((data[y1, x1] + data[y2, x1] + 8) >> 1) & 0xF8); + // } + // } + // } + // else + // { + // for (int y = 0; y < height; y++) + // { + // for (int x = 0; x < width; x++) + // { + // int x1 = MathUtil.Clamp(x + (srcX >> 1), 0, data.GetLength(1) - 1); + // int x2 = MathUtil.Clamp(x + (srcX >> 1) + 1, 0, data.GetLength(1) - 1); + // int y1 = MathUtil.Clamp(y + (srcY >> 1), 0, data.GetLength(0) - 1); + // int y2 = MathUtil.Clamp(y + (srcY >> 1) + 1, 0, data.GetLength(0) - 1); + // int a = data[y1, x1] >> 3 << 1; + // if (a != 0) + // a++; + // int b = data[y2, x2] >> 3 << 1; + // if (b != 0) + // b++; + // result[y * width + x] = (byte) ((a * 16 + b * 16) >> 6 << 3); + // // result[y * width + x] = (byte) (((data[y1, x1] + data[y2, x2] + 8) >> 1) & 0xF8); + // } + // } + // } + // + // return result; + // } + + public static void SetTile(byte[,] data, int dstX, int dstY, int width, int height, byte[] src) + { + for (int y = 0; y < height; y++) + for (int x = 0; x < width; x++) + data[y + dstY, x + dstX] = src[y * width + x]; + } + + public static void SetTile(byte[] data, int stride, int dstX, int dstY, int width, int height, int step, + byte[] src) + { + for (int y = 0; y < height; y++) + for (int x = 0; x < width; x++) + data[(y * step + dstY) * stride + x * step + dstX] = src[y * width + x]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SetTile2x2Step2(byte[] data, int stride, int dstX, int dstY, byte[] src) + { + data[dstY * stride + dstX] = src[0]; + data[dstY * stride + dstX + 2] = src[1]; + data[(dstY + 2) * stride + dstX] = src[2]; + data[(dstY + 2) * stride + dstX + 2] = src[3]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void SetTile8(byte[] data, int stride, int dstX, int dstY, byte[] src) + { + fixed (byte* pSrc = &src[0], pDst = &data[dstY * stride + dstX]) + { + *(ulong*)(pDst) = ((ulong*)pSrc)[0]; + *(ulong*)(pDst + 1 * stride) = ((ulong*)pSrc)[1]; + *(ulong*)(pDst + 2 * stride) = ((ulong*)pSrc)[2]; + *(ulong*)(pDst + 3 * stride) = ((ulong*)pSrc)[3]; + *(ulong*)(pDst + 4 * stride) = ((ulong*)pSrc)[4]; + *(ulong*)(pDst + 5 * stride) = ((ulong*)pSrc)[5]; + *(ulong*)(pDst + 6 * stride) = ((ulong*)pSrc)[6]; + *(ulong*)(pDst + 7 * stride) = ((ulong*)pSrc)[7]; + } + } + + // public static void SetTile(byte[] data, int stride, int dstX, int dstY, int width, int height, byte[,] src) + // { + // for (int y = 0; y < height; y++) + // for (int x = 0; x < width; x++) + // data[(y + dstY) * stride + x + dstX] = src[y, x]; + // } + + public static void SetTile(byte[] data, int stride, int dstX, int dstY, int width, int height, int step, + byte[,] src) + { + for (int y = 0; y < height; y++) + for (int x = 0; x < width; x++) + data[(y * step + dstY) * stride + x * step + dstX] = src[y, x]; + } + + public static unsafe byte[] GetBlockPixels16x16(byte[] Data, int X, int Y, int Stride, int Offset) + { + byte[] values = new byte[256]; + fixed (byte* pVals = &values[0]) + { + ulong* pLVals = (ulong*)pVals; + for (int y3 = 0; y3 < 16; y3++) + { + fixed (byte* pData = &Data[(Y + y3) * Stride + X + Offset]) + { + *pLVals++ = *((ulong*)pData); + *pLVals++ = *((ulong*)(pData + 8)); + } + } + } + + return values; + } + + public static unsafe byte[] GetBlockPixels8x8(byte[] Data, int X, int Y, int Stride, int Offset) + { + byte[] values = new byte[64]; + fixed (byte* pVals = &values[0], pData = &Data[Y * Stride + X + Offset]) + { + ulong* pLVals = (ulong*)pVals; + *pLVals++ = *((ulong*)pData); + *pLVals++ = *((ulong*)(pData + Stride)); + *pLVals++ = *((ulong*)(pData + Stride * 2)); + *pLVals++ = *((ulong*)(pData + Stride * 3)); + *pLVals++ = *((ulong*)(pData + Stride * 4)); + *pLVals++ = *((ulong*)(pData + Stride * 5)); + *pLVals++ = *((ulong*)(pData + Stride * 6)); + *pLVals++ = *((ulong*)(pData + Stride * 7)); + /*ulong* pLVals = (ulong*)pVals; + for (int y3 = 0; y3 < 8; y3++) + { + fixed (byte* pData = &Data[(Y + y3) * Stride + X + Offset]) + { + *pLVals++ = *((ulong*)pData); + } + }*/ + } + + return values; + } + + public static unsafe byte[] GetBlockPixels4x4(byte[] Data, int X, int Y, int Stride, int Offset) + { + byte[] values = new byte[16]; + fixed (byte* pVals = &values[0], pData = &Data[Y * Stride + X + Offset]) + { + uint* pLVals = (uint*)pVals; + *pLVals++ = *((uint*)pData); + *pLVals++ = *((uint*)(pData + Stride)); + *pLVals++ = *((uint*)(pData + Stride * 2)); + *pLVals++ = *((uint*)(pData + Stride * 3)); + } + + return values; + } + + public static unsafe void SetBlockPixels4x4(byte[] Data, int X, int Y, int Stride, int Offset, byte[] Values) + { + fixed (byte* pVals = &Values[0], pData = &Data[Y * Stride + X + Offset]) + { + uint* pLVals = (uint*)pVals; + *((uint*)pData) = *pLVals++; + *((uint*)(pData + Stride)) = *pLVals++; + *((uint*)(pData + Stride * 2)) = *pLVals++; + *((uint*)(pData + Stride * 3)) = *pLVals++; + } + } + + public static unsafe void SetBlockPixels8x8(byte[] Data, int X, int Y, int Stride, int Offset, byte[] Values) + { + fixed (byte* pVals = &Values[0], pData = &Data[Y * Stride + X + Offset]) + { + ulong* pLVals = (ulong*)pVals; + *((ulong*)pData) = *pLVals++; + *((ulong*)(pData + Stride)) = *pLVals++; + *((ulong*)(pData + Stride * 2)) = *pLVals++; + *((ulong*)(pData + Stride * 3)) = *pLVals++; + *((ulong*)(pData + Stride * 4)) = *pLVals++; + *((ulong*)(pData + Stride * 5)) = *pLVals++; + *((ulong*)(pData + Stride * 6)) = *pLVals++; + *((ulong*)(pData + Stride * 7)) = *pLVals++; + } + } + + + public static unsafe int Sad64(ReadOnlySpan a, ReadOnlySpan b) + { + fixed (byte* pA = a, pB = b) + { + var a0 = Avx.LoadVector256(pA); + var b0 = Avx.LoadVector256(pB); + var sad0 = Avx2.SumAbsoluteDifferences(a0, b0); + var a1 = Avx.LoadVector256(pA + 32); + var b1 = Avx.LoadVector256(pB + 32); + var sad1 = Avx2.SumAbsoluteDifferences(a1, b1); + var diff = Avx2.Add(sad0.AsInt32(), sad1.AsInt32()); + var diff2 = Sse2.Add(diff.GetLower(), diff.GetUpper()); + return diff2.GetElement(0) + diff2.GetElement(2); + } + } + + public static unsafe ulong Sad(ReadOnlySpan a, ReadOnlySpan b) + { + var sad = Vector256.Zero; + ulong result; + fixed (byte* pA0 = a, pB0 = b) + { + int i; + for (i = 0; i + 31 < a.Length; i += 32) + { + var a0 = Avx.LoadVector256(pA0 + i); + var b0 = Avx.LoadVector256(pB0 + i); + sad = Avx2.Add(sad, Avx2.SumAbsoluteDifferences(a0, b0).AsUInt64()); + } + + var result2 = Sse2.Add(sad.GetLower(), sad.GetUpper()); + result = result2.GetElement(0) + result2.GetElement(1); + for (; i < a.Length; i++) + result += (ulong)Math.Abs(pA0[i] - pB0[i]); + } + + return result; + } + } +} \ No newline at end of file diff --git a/Gericom.FastVideoDS/Vlc.cs b/Gericom.FastVideoDS/Vlc.cs new file mode 100644 index 0000000..7dab7a2 --- /dev/null +++ b/Gericom.FastVideoDS/Vlc.cs @@ -0,0 +1,207 @@ +using Gericom.FastVideoDS.Bitstream; + +namespace Gericom.FastVideoDS +{ + public static class Vlc + { + public static readonly int[] BitLengthTable; + + static Vlc() + { + BitLengthTable = new int[2 * 64 * 128]; + ushort[] tabA = VlcTables.TableA; + byte[] tabB = VlcTables.TableB; + for (int last = 0; last <= 1; last++) + { + for (int skip = 0; skip < 64; skip++) + { + for (int value = -64; value < 64; value++) + { + int val = value; + if (val < 0) + val = -val; + if (val <= 31) + { + int idx = VlcTables.TableARefLinear[val * 64 * 2 + skip * 2 + last]; + if (idx >= 0) + { + BitLengthTable[last * 128 * 64 + skip * 128 + (value + 64)] = tabA[idx] & 0xF; + continue; + } + + int newskip = skip - tabB[(val | (last << 6)) + 0x80]; + if (newskip >= 0) + { + idx = VlcTables.TableARefLinear[val * 64 * 2 + newskip * 2 + last]; + if (idx >= 0) + { + BitLengthTable[last * 128 * 64 + skip * 128 + (value + 64)] = 9 + (tabA[idx] & 0xF); + continue; + } + } + } + + int newval = val - tabB[skip | (last << 6)]; + if (newval >= 0 && newval <= 31) + { + int idx = VlcTables.TableARefLinear[newval * 64 * 2 + skip * 2 + last]; + if (idx >= 0) + { + BitLengthTable[last * 128 * 64 + skip * 128 + (value + 64)] = 8 + (tabA[idx] & 0xF); + continue; + } + } + + BitLengthTable[last * 128 * 64 + skip * 128 + (value + 64)] = 28; + } + } + } + } + + public static int CalcDctBitCount(int[] dct) + { + int lastNonZero = 0; + for (int i = dct.Length - 1; i >= 0; i--) + { + if (dct[i] != 0) + { + lastNonZero = i; + break; + } + } + + return CalcDctBitCount(dct, lastNonZero); + } + + public static int CalcDctBitCount(int[] dct, int lastNonZero) + { + int bitCount = 0; + + int skip = 0; + for (int i = 0; i < lastNonZero; i++) + { + if (dct[i] == 0) + { + skip++; + continue; + } + + int val = dct[i]; + if (val + 64 < 128) + bitCount += BitLengthTable[(skip * 128) + (val + 64)]; + else + bitCount += 28; + skip = 0; + } + + if (dct[lastNonZero] + 64 < 128) + bitCount += BitLengthTable[128 * 64 + (skip * 128) + (dct[lastNonZero] + 64)]; + else + bitCount += 28; + + return bitCount; + } + + public static void EncodeDct(int[] dct, BitWriter b) + { + ushort[] tabA = VlcTables.TableA; + byte[] tabB = VlcTables.TableB; + int lastNonZero = 0; + for (int i = 0; i < dct.Length; i++) + { + if (dct[i] != 0) + lastNonZero = i; + } + + int skip = 0; + for (int i = 0; i < dct.Length; i++) + { + if (dct[i] == 0 && lastNonZero != 0) + { + skip++; + continue; + } + + int val = dct[i]; + + if (val < 0) val = -val; + if (val <= 31) + { + int idx = VlcTables.TableARefLinear[val * 64 * 2 + skip * 2 + ((i == lastNonZero) ? 1 : 0)]; + if (idx >= 0) + { + int nrbits = (tabA[idx] & 0xF); + uint tidx = (uint)idx; + if (nrbits < 12) + tidx >>= (12 - nrbits); + else if (nrbits > 12) + tidx <<= (nrbits - 12); + if (dct[i] < 0) tidx |= 1; + b.WriteBits((uint)tidx, nrbits); + skip = 0; + goto end; + } + + int newskip = skip - tabB[(val | (((i == lastNonZero) ? 1 : 0) << 6)) + 0x80]; + if (newskip >= 0) + { + idx = VlcTables.TableARefLinear[ + val * 64 * 2 + newskip * 2 + + ((i == lastNonZero) ? 1 : 0)]; + if (idx >= 0) + { + b.WriteBits(3, 7); + b.WriteBits(1, 1); + b.WriteBits(0, 1); + int nrbits = (tabA[idx] & 0xF); + uint tidx = (uint)idx; + if (nrbits < 12) + tidx >>= (12 - nrbits); + else if (nrbits > 12) + tidx <<= (nrbits - 12); + if (dct[i] < 0) tidx |= 1; + b.WriteBits((uint)tidx, nrbits); + skip = 0; + goto end; + } + } + } + + int newval = val - tabB[skip | (((i == lastNonZero) ? 1 : 0) << 6)]; + if (newval >= 0 && newval <= 31) + { + int idx = VlcTables.TableARefLinear[newval * 64 * 2 + skip * 2 + ((i == lastNonZero) ? 1 : 0)]; + if (idx >= 0) + { + b.WriteBits(3, 7); + b.WriteBits(0, 1); + int nrbits = (tabA[idx] & 0xF); + uint tidx = (uint)idx; + if (nrbits < 12) + tidx >>= (12 - nrbits); + else if (nrbits > 12) + tidx <<= (nrbits - 12); + if (dct[i] < 0) tidx |= 1; + b.WriteBits((uint)tidx, nrbits); + skip = 0; + goto end; + } + } + + b.WriteBits(3, 7); + b.WriteBits(1, 1); + b.WriteBits(1, 1); + if (i == lastNonZero) + b.WriteBits(1, 1); + else + b.WriteBits(0, 1); + b.WriteBits((uint)skip, 6); + skip = 0; + b.WriteBits((uint)dct[i], 12); + end: + if (i == lastNonZero) + break; + } + } + } +} \ No newline at end of file diff --git a/Gericom.FastVideoDS/VlcTables.cs b/Gericom.FastVideoDS/VlcTables.cs new file mode 100644 index 0000000..a157fc1 --- /dev/null +++ b/Gericom.FastVideoDS/VlcTables.cs @@ -0,0 +1,327 @@ +namespace Gericom.FastVideoDS +{ + public static class VlcTables + { + public static readonly ushort[] TableA = + { + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x807C, 0x807C, 0x806C, 0x806C, 0x016C, 0x016C, 0x015C, 0x015C, + 0x842B, 0x842B, 0x842B, 0x842B, 0x823B, 0x823B, 0x823B, 0x823B, 0x805B, 0x805B, 0x805B, 0x805B, 0x1A1B, 0x1A1B, 0x1A1B, 0x1A1B, + 0x0A3B, 0x0A3B, 0x0A3B, 0x0A3B, 0x102B, 0x102B, 0x102B, 0x102B, 0x083B, 0x083B, 0x083B, 0x083B, 0x064B, 0x064B, 0x064B, 0x064B, + 0x044B, 0x044B, 0x044B, 0x044B, 0x027B, 0x027B, 0x027B, 0x027B, 0x014B, 0x014B, 0x014B, 0x014B, 0x013B, 0x013B, 0x013B, 0x013B, + 0x017C, 0x017C, 0x018C, 0x018C, 0x028C, 0x028C, 0x122C, 0x122C, 0x862C, 0x862C, 0x882C, 0x882C, 0x9E1C, 0x9E1C, 0xA01C, 0xA01C, + 0x019D, 0x01AD, 0x01BD, 0x029D, 0x0C3D, 0x02AD, 0x045D, 0x0E3D, 0x1C1D, 0x808D, 0x8A2D, 0x8C2D, 0xA21D, 0xA41D, 0xA61D, 0xA81D, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x012B, 0x012B, 0x012B, 0x012B, 0x011B, 0x011B, 0x011B, 0x011B, 0x9C1A, 0x9C1A, 0x9C1A, 0x9C1A, 0x9C1A, 0x9C1A, 0x9C1A, 0x9C1A, + 0x9A1A, 0x9A1A, 0x9A1A, 0x9A1A, 0x9A1A, 0x9A1A, 0x9A1A, 0x9A1A, 0x981A, 0x981A, 0x981A, 0x981A, 0x981A, 0x981A, 0x981A, 0x981A, + 0x961A, 0x961A, 0x961A, 0x961A, 0x961A, 0x961A, 0x961A, 0x961A, 0x941A, 0x941A, 0x941A, 0x941A, 0x941A, 0x941A, 0x941A, 0x941A, + 0x822A, 0x822A, 0x822A, 0x822A, 0x822A, 0x822A, 0x822A, 0x822A, 0x804A, 0x804A, 0x804A, 0x804A, 0x804A, 0x804A, 0x804A, 0x804A, + 0x181A, 0x181A, 0x181A, 0x181A, 0x181A, 0x181A, 0x181A, 0x181A, 0x161A, 0x161A, 0x161A, 0x161A, 0x161A, 0x161A, 0x161A, 0x161A, + 0x0E2A, 0x0E2A, 0x0E2A, 0x0E2A, 0x0E2A, 0x0E2A, 0x0E2A, 0x0E2A, 0x0C2A, 0x0C2A, 0x0C2A, 0x0C2A, 0x0C2A, 0x0C2A, 0x0C2A, 0x0C2A, + 0x0A2A, 0x0A2A, 0x0A2A, 0x0A2A, 0x0A2A, 0x0A2A, 0x0A2A, 0x0A2A, 0x063A, 0x063A, 0x063A, 0x063A, 0x063A, 0x063A, 0x063A, 0x063A, + 0x043A, 0x043A, 0x043A, 0x043A, 0x043A, 0x043A, 0x043A, 0x043A, 0x026A, 0x026A, 0x026A, 0x026A, 0x026A, 0x026A, 0x026A, 0x026A, + 0x025A, 0x025A, 0x025A, 0x025A, 0x025A, 0x025A, 0x025A, 0x025A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, + 0x082A, 0x082A, 0x082A, 0x082A, 0x082A, 0x082A, 0x082A, 0x082A, 0x00FA, 0x00FA, 0x00FA, 0x00FA, 0x00FA, 0x00FA, 0x00FA, 0x00FA, + 0x00EA, 0x00EA, 0x00EA, 0x00EA, 0x00EA, 0x00EA, 0x00EA, 0x00EA, 0x00DA, 0x00DA, 0x00DA, 0x00DA, 0x00DA, 0x00DA, 0x00DA, 0x00DA, + 0x9019, 0x9019, 0x9019, 0x9019, 0x9019, 0x9019, 0x9019, 0x9019, 0x9019, 0x9019, 0x9019, 0x9019, 0x9019, 0x9019, 0x9019, 0x9019, + 0x8E19, 0x8E19, 0x8E19, 0x8E19, 0x8E19, 0x8E19, 0x8E19, 0x8E19, 0x8E19, 0x8E19, 0x8E19, 0x8E19, 0x8E19, 0x8E19, 0x8E19, 0x8E19, + 0x8C19, 0x8C19, 0x8C19, 0x8C19, 0x8C19, 0x8C19, 0x8C19, 0x8C19, 0x8C19, 0x8C19, 0x8C19, 0x8C19, 0x8C19, 0x8C19, 0x8C19, 0x8C19, + 0x8039, 0x8039, 0x8039, 0x8039, 0x8039, 0x8039, 0x8039, 0x8039, 0x8039, 0x8039, 0x8039, 0x8039, 0x8039, 0x8039, 0x8039, 0x8039, + 0x1419, 0x1419, 0x1419, 0x1419, 0x1419, 0x1419, 0x1419, 0x1419, 0x1419, 0x1419, 0x1419, 0x1419, 0x1419, 0x1419, 0x1419, 0x1419, + 0x1219, 0x1219, 0x1219, 0x1219, 0x1219, 0x1219, 0x1219, 0x1219, 0x1219, 0x1219, 0x1219, 0x1219, 0x1219, 0x1219, 0x1219, 0x1219, + 0x1019, 0x1019, 0x1019, 0x1019, 0x1019, 0x1019, 0x1019, 0x1019, 0x1019, 0x1019, 0x1019, 0x1019, 0x1019, 0x1019, 0x1019, 0x1019, + 0x9219, 0x9219, 0x9219, 0x9219, 0x9219, 0x9219, 0x9219, 0x9219, 0x9219, 0x9219, 0x9219, 0x9219, 0x9219, 0x9219, 0x9219, 0x9219, + 0x0629, 0x0629, 0x0629, 0x0629, 0x0629, 0x0629, 0x0629, 0x0629, 0x0629, 0x0629, 0x0629, 0x0629, 0x0629, 0x0629, 0x0629, 0x0629, + 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, + 0x00C9, 0x00C9, 0x00C9, 0x00C9, 0x00C9, 0x00C9, 0x00C9, 0x00C9, 0x00C9, 0x00C9, 0x00C9, 0x00C9, 0x00C9, 0x00C9, 0x00C9, 0x00C9, + 0x00B9, 0x00B9, 0x00B9, 0x00B9, 0x00B9, 0x00B9, 0x00B9, 0x00B9, 0x00B9, 0x00B9, 0x00B9, 0x00B9, 0x00B9, 0x00B9, 0x00B9, 0x00B9, + 0x00A9, 0x00A9, 0x00A9, 0x00A9, 0x00A9, 0x00A9, 0x00A9, 0x00A9, 0x00A9, 0x00A9, 0x00A9, 0x00A9, 0x00A9, 0x00A9, 0x00A9, 0x00A9, + 0x8818, 0x8818, 0x8818, 0x8818, 0x8818, 0x8818, 0x8818, 0x8818, 0x8818, 0x8818, 0x8818, 0x8818, 0x8818, 0x8818, 0x8818, 0x8818, + 0x8818, 0x8818, 0x8818, 0x8818, 0x8818, 0x8818, 0x8818, 0x8818, 0x8818, 0x8818, 0x8818, 0x8818, 0x8818, 0x8818, 0x8818, 0x8818, + 0x8618, 0x8618, 0x8618, 0x8618, 0x8618, 0x8618, 0x8618, 0x8618, 0x8618, 0x8618, 0x8618, 0x8618, 0x8618, 0x8618, 0x8618, 0x8618, + 0x8618, 0x8618, 0x8618, 0x8618, 0x8618, 0x8618, 0x8618, 0x8618, 0x8618, 0x8618, 0x8618, 0x8618, 0x8618, 0x8618, 0x8618, 0x8618, + 0x0C18, 0x0C18, 0x0C18, 0x0C18, 0x0C18, 0x0C18, 0x0C18, 0x0C18, 0x0C18, 0x0C18, 0x0C18, 0x0C18, 0x0C18, 0x0C18, 0x0C18, 0x0C18, + 0x0C18, 0x0C18, 0x0C18, 0x0C18, 0x0C18, 0x0C18, 0x0C18, 0x0C18, 0x0C18, 0x0C18, 0x0C18, 0x0C18, 0x0C18, 0x0C18, 0x0C18, 0x0C18, + 0x8A18, 0x8A18, 0x8A18, 0x8A18, 0x8A18, 0x8A18, 0x8A18, 0x8A18, 0x8A18, 0x8A18, 0x8A18, 0x8A18, 0x8A18, 0x8A18, 0x8A18, 0x8A18, + 0x8A18, 0x8A18, 0x8A18, 0x8A18, 0x8A18, 0x8A18, 0x8A18, 0x8A18, 0x8A18, 0x8A18, 0x8A18, 0x8A18, 0x8A18, 0x8A18, 0x8A18, 0x8A18, + 0x0E18, 0x0E18, 0x0E18, 0x0E18, 0x0E18, 0x0E18, 0x0E18, 0x0E18, 0x0E18, 0x0E18, 0x0E18, 0x0E18, 0x0E18, 0x0E18, 0x0E18, 0x0E18, + 0x0E18, 0x0E18, 0x0E18, 0x0E18, 0x0E18, 0x0E18, 0x0E18, 0x0E18, 0x0E18, 0x0E18, 0x0E18, 0x0E18, 0x0E18, 0x0E18, 0x0E18, 0x0E18, + 0x0428, 0x0428, 0x0428, 0x0428, 0x0428, 0x0428, 0x0428, 0x0428, 0x0428, 0x0428, 0x0428, 0x0428, 0x0428, 0x0428, 0x0428, 0x0428, + 0x0428, 0x0428, 0x0428, 0x0428, 0x0428, 0x0428, 0x0428, 0x0428, 0x0428, 0x0428, 0x0428, 0x0428, 0x0428, 0x0428, 0x0428, 0x0428, + 0x0238, 0x0238, 0x0238, 0x0238, 0x0238, 0x0238, 0x0238, 0x0238, 0x0238, 0x0238, 0x0238, 0x0238, 0x0238, 0x0238, 0x0238, 0x0238, + 0x0238, 0x0238, 0x0238, 0x0238, 0x0238, 0x0238, 0x0238, 0x0238, 0x0238, 0x0238, 0x0238, 0x0238, 0x0238, 0x0238, 0x0238, 0x0238, + 0x0098, 0x0098, 0x0098, 0x0098, 0x0098, 0x0098, 0x0098, 0x0098, 0x0098, 0x0098, 0x0098, 0x0098, 0x0098, 0x0098, 0x0098, 0x0098, + 0x0098, 0x0098, 0x0098, 0x0098, 0x0098, 0x0098, 0x0098, 0x0098, 0x0098, 0x0098, 0x0098, 0x0098, 0x0098, 0x0098, 0x0098, 0x0098, + 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, + 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, + 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, + 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, 0x8027, + 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, + 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, + 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, + 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, 0x0A17, + 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, + 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, + 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, + 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, 0x8417, + 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, + 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, + 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, + 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, 0x8217, + 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, + 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, + 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, + 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, 0x0817, + 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, + 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, + 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, + 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, 0x0617, + 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, + 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, + 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, + 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, 0x0087, + 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, + 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, + 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, + 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, 0x0077, + 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, + 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, + 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, + 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, 0x0227, + 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, + 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, + 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, + 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, 0x0067, + 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, + 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, + 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, + 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, + 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, + 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, + 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, + 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, 0x0416, + 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, + 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, + 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, + 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, + 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, + 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, + 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, + 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, 0x0056, + 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, + 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, + 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, + 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, + 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, + 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, + 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, + 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, 0x0046, + 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, + 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, + 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, + 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, + 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, + 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, + 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, + 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, + 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, + 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, + 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, + 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, + 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, + 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, + 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, + 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, 0x8015, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, 0x0013, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, 0x0024, + 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, + 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, + 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, + 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, + 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, + 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, + 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, + 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, + 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, + 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, + 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, + 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, + 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, + 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, + 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, + 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, 0x0215, + 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, + 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, + 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, + 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, + 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, + 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, + 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, + 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, + 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, + 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, + 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, + 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, + 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, + 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, + 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, + 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035, 0x0035 + }; + + public static readonly byte[] TableB = + { + 0x1B, 0x0A, 0x05, 0x04, + 0x03, 0x03, 0x03, 0x03, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x0F, 0x0A, 0x08, 0x04, 0x03, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x15, 0x07, 0x02, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 + }; + + public static readonly int[] TableARefLinear = + { + 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 2048, 1792, 3584, 960, 1408, 896, 1088, 544, 1024, 512, 832, 608, 576, 336, 640, 320, 400, 304, 384, 416, 368, 168, 200, 160, 192, 152, 28, 144, 88, 136, -1, 76, -1, 78, -1, 92, -1, 93, -1, 94, -1, 95, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 3072, 768, 1280, 176, 672, 16, 432, 72, 272, 74, 224, 90, 216, 91, 208, -1, 36, -1, 70, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 3840, 352, 704, 20, 240, -1, 232, -1, 40, -1, 32, -1, 84, -1, 87, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 1664, 184, 448, -1, 48, -1, 44, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 1536, 24, 256, -1, 86, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 1344, 10, 248, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 1216, 8, 52, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 1152, 89, 68, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 736, -1, 83, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 496, -1, 85, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 480, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 464, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 296, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 288, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 280, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 264, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 132, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 128, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 60, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 56, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 14, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 12, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 64, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 66, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 80, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 81, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 82, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + }; + } +}