diff options
Diffstat (limited to 'libfreerdp/codec/rfx_rlgr.c')
-rw-r--r-- | libfreerdp/codec/rfx_rlgr.c | 772 |
1 files changed, 772 insertions, 0 deletions
diff --git a/libfreerdp/codec/rfx_rlgr.c b/libfreerdp/codec/rfx_rlgr.c new file mode 100644 index 0000000..da1b63f --- /dev/null +++ b/libfreerdp/codec/rfx_rlgr.c @@ -0,0 +1,772 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RemoteFX Codec Library - RLGR + * + * Copyright 2011 Vic Lee + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This implementation of RLGR refers to + * [MS-RDPRFX] 3.1.8.1.7.3 RLGR1/RLGR3 Pseudocode + */ + +#include <freerdp/config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <winpr/crt.h> +#include <winpr/print.h> +#include <winpr/sysinfo.h> +#include <winpr/bitstream.h> +#include <winpr/intrin.h> + +#include "rfx_bitstream.h" + +#include "rfx_rlgr.h" + +/* Constants used in RLGR1/RLGR3 algorithm */ +#define KPMAX (80) /* max value for kp or krp */ +#define LSGR (3) /* shift count to convert kp to k */ +#define UP_GR (4) /* increase in kp after a zero run in RL mode */ +#define DN_GR (6) /* decrease in kp after a nonzero symbol in RL mode */ +#define UQ_GR (3) /* increase in kp after nonzero symbol in GR mode */ +#define DQ_GR (3) /* decrease in kp after zero symbol in GR mode */ + +/* Returns the least number of bits required to represent a given value */ +#define GetMinBits(_val, _nbits) \ + do \ + { \ + UINT32 _v = _val; \ + _nbits = 0; \ + while (_v) \ + { \ + _v >>= 1; \ + _nbits++; \ + } \ + } while (0) + +/* + * Update the passed parameter and clamp it to the range [0, KPMAX] + * Return the value of parameter right-shifted by LSGR + */ +#define UpdateParam(_param, _deltaP, _k) \ + do \ + { \ + _param += _deltaP; \ + if (_param > KPMAX) \ + _param = KPMAX; \ + if (_param < 0) \ + _param = 0; \ + _k = (_param >> LSGR); \ + } while (0) + +static BOOL g_LZCNT = FALSE; + +static INIT_ONCE rfx_rlgr_init_once = INIT_ONCE_STATIC_INIT; + +static BOOL CALLBACK rfx_rlgr_init(PINIT_ONCE once, PVOID param, PVOID* context) +{ + g_LZCNT = IsProcessorFeaturePresentEx(PF_EX_LZCNT); + return TRUE; +} + +static INLINE UINT32 lzcnt_s(UINT32 x) +{ + if (!x) + return 32; + + if (!g_LZCNT) + { + UINT32 y = 0; + UINT32 n = 32; + y = x >> 16; + if (y != 0) + { + WINPR_ASSERT(n >= 16); + n = n - 16; + x = y; + } + y = x >> 8; + if (y != 0) + { + WINPR_ASSERT(n >= 8); + n = n - 8; + x = y; + } + y = x >> 4; + if (y != 0) + { + WINPR_ASSERT(n >= 4); + n = n - 4; + x = y; + } + y = x >> 2; + if (y != 0) + { + WINPR_ASSERT(n >= 2); + n = n - 2; + x = y; + } + y = x >> 1; + if (y != 0) + { + WINPR_ASSERT(n >= 2); + return n - 2; + } + + WINPR_ASSERT(n >= x); + return n - x; + } + + return __lzcnt(x); +} + +int rfx_rlgr_decode(RLGR_MODE mode, const BYTE* WINPR_RESTRICT pSrcData, UINT32 SrcSize, + INT16* WINPR_RESTRICT pDstData, UINT32 rDstSize) +{ + int vk = 0; + size_t run = 0; + int cnt = 0; + size_t size = 0; + int nbits = 0; + size_t offset = 0; + INT16 mag = 0; + UINT32 k = 0; + INT32 kp = 0; + UINT32 kr = 0; + INT32 krp = 0; + UINT16 code = 0; + UINT32 sign = 0; + UINT32 nIdx = 0; + UINT32 val1 = 0; + UINT32 val2 = 0; + INT16* pOutput = NULL; + wBitStream* bs = NULL; + wBitStream s_bs = { 0 }; + const SSIZE_T DstSize = rDstSize; + + InitOnceExecuteOnce(&rfx_rlgr_init_once, rfx_rlgr_init, NULL, NULL); + + k = 1; + kp = k << LSGR; + + kr = 1; + krp = kr << LSGR; + + if ((mode != RLGR1) && (mode != RLGR3)) + mode = RLGR1; + + if (!pSrcData || !SrcSize) + return -1; + + if (!pDstData || !DstSize) + return -1; + + pOutput = pDstData; + + bs = &s_bs; + + BitStream_Attach(bs, pSrcData, SrcSize); + BitStream_Fetch(bs); + + while ((BitStream_GetRemainingLength(bs) > 0) && ((pOutput - pDstData) < DstSize)) + { + if (k) + { + /* Run-Length (RL) Mode */ + + run = 0; + + /* count number of leading 0s */ + + cnt = lzcnt_s(bs->accumulator); + + nbits = BitStream_GetRemainingLength(bs); + + if (cnt > nbits) + cnt = nbits; + + vk = cnt; + + while ((cnt == 32) && (BitStream_GetRemainingLength(bs) > 0)) + { + BitStream_Shift32(bs); + + cnt = lzcnt_s(bs->accumulator); + + nbits = BitStream_GetRemainingLength(bs); + + if (cnt > nbits) + cnt = nbits; + + vk += cnt; + } + + BitStream_Shift(bs, (vk % 32)); + + if (BitStream_GetRemainingLength(bs) < 1) + break; + + BitStream_Shift(bs, 1); + + while (vk--) + { + const UINT32 add = (1 << k); /* add (1 << k) to run length */ + run += add; + + /* update k, kp params */ + + kp += UP_GR; + + if (kp > KPMAX) + kp = KPMAX; + + k = kp >> LSGR; + } + + /* next k bits contain run length remainder */ + + if (BitStream_GetRemainingLength(bs) < k) + break; + + bs->mask = ((1 << k) - 1); + run += ((bs->accumulator >> (32 - k)) & bs->mask); + BitStream_Shift(bs, k); + + /* read sign bit */ + + if (BitStream_GetRemainingLength(bs) < 1) + break; + + sign = (bs->accumulator & 0x80000000) ? 1 : 0; + BitStream_Shift(bs, 1); + + /* count number of leading 1s */ + + cnt = lzcnt_s(~(bs->accumulator)); + + nbits = BitStream_GetRemainingLength(bs); + + if (cnt > nbits) + cnt = nbits; + + vk = cnt; + + while ((cnt == 32) && (BitStream_GetRemainingLength(bs) > 0)) + { + BitStream_Shift32(bs); + + cnt = lzcnt_s(~(bs->accumulator)); + + nbits = BitStream_GetRemainingLength(bs); + + if (cnt > nbits) + cnt = nbits; + + vk += cnt; + } + + BitStream_Shift(bs, (vk % 32)); + + if (BitStream_GetRemainingLength(bs) < 1) + break; + + BitStream_Shift(bs, 1); + + /* next kr bits contain code remainder */ + + if (BitStream_GetRemainingLength(bs) < kr) + break; + + bs->mask = ((1 << kr) - 1); + if (kr > 0) + code = (UINT16)((bs->accumulator >> (32 - kr)) & bs->mask); + else + code = 0; + BitStream_Shift(bs, kr); + + /* add (vk << kr) to code */ + + code |= (vk << kr); + + if (!vk) + { + /* update kr, krp params */ + + krp -= 2; + + if (krp < 0) + krp = 0; + + kr = krp >> LSGR; + } + else if (vk != 1) + { + /* update kr, krp params */ + + krp += vk; + + if (krp > KPMAX) + krp = KPMAX; + + kr = krp >> LSGR; + } + + /* update k, kp params */ + + kp -= DN_GR; + + if (kp < 0) + kp = 0; + + k = kp >> LSGR; + + /* compute magnitude from code */ + + if (sign) + mag = ((INT16)(code + 1)) * -1; + else + mag = (INT16)(code + 1); + + /* write to output stream */ + + offset = (pOutput - pDstData); + size = run; + + if ((offset + size) > rDstSize) + size = DstSize - offset; + + if (size) + { + ZeroMemory(pOutput, size * sizeof(INT16)); + pOutput += size; + } + + if ((pOutput - pDstData) < DstSize) + { + *pOutput = mag; + pOutput++; + } + } + else + { + /* Golomb-Rice (GR) Mode */ + + /* count number of leading 1s */ + + cnt = lzcnt_s(~(bs->accumulator)); + + nbits = BitStream_GetRemainingLength(bs); + + if (cnt > nbits) + cnt = nbits; + + vk = cnt; + + while ((cnt == 32) && (BitStream_GetRemainingLength(bs) > 0)) + { + BitStream_Shift32(bs); + + cnt = lzcnt_s(~(bs->accumulator)); + + nbits = BitStream_GetRemainingLength(bs); + + if (cnt > nbits) + cnt = nbits; + + vk += cnt; + } + + BitStream_Shift(bs, (vk % 32)); + + if (BitStream_GetRemainingLength(bs) < 1) + break; + + BitStream_Shift(bs, 1); + + /* next kr bits contain code remainder */ + + if (BitStream_GetRemainingLength(bs) < kr) + break; + + bs->mask = ((1 << kr) - 1); + if (kr > 0) + code = (UINT16)((bs->accumulator >> (32 - kr)) & bs->mask); + else + code = 0; + BitStream_Shift(bs, kr); + + /* add (vk << kr) to code */ + + code |= (vk << kr); + + if (!vk) + { + /* update kr, krp params */ + + krp -= 2; + + if (krp < 0) + krp = 0; + + kr = krp >> LSGR; + } + else if (vk != 1) + { + /* update kr, krp params */ + + krp += vk; + + if (krp > KPMAX) + krp = KPMAX; + + kr = krp >> LSGR; + } + + if (mode == RLGR1) /* RLGR1 */ + { + if (!code) + { + /* update k, kp params */ + + kp += UQ_GR; + + if (kp > KPMAX) + kp = KPMAX; + + k = kp >> LSGR; + + mag = 0; + } + else + { + /* update k, kp params */ + + kp -= DQ_GR; + + if (kp < 0) + kp = 0; + + k = kp >> LSGR; + + /* + * code = 2 * mag - sign + * sign + code = 2 * mag + */ + + if (code & 1) + mag = ((INT16)((code + 1) >> 1)) * -1; + else + mag = (INT16)(code >> 1); + } + + if ((pOutput - pDstData) < DstSize) + { + *pOutput = mag; + pOutput++; + } + } + else if (mode == RLGR3) /* RLGR3 */ + { + nIdx = 0; + + if (code) + { + mag = (UINT32)code; + nIdx = 32 - lzcnt_s(mag); + } + + if (BitStream_GetRemainingLength(bs) < nIdx) + break; + + bs->mask = ((1 << nIdx) - 1); + if (nIdx > 0) + val1 = ((bs->accumulator >> (32 - nIdx)) & bs->mask); + else + val1 = 0; + BitStream_Shift(bs, nIdx); + + val2 = code - val1; + + if (val1 && val2) + { + /* update k, kp params */ + + kp -= (2 * DQ_GR); + + if (kp < 0) + kp = 0; + + k = kp >> LSGR; + } + else if (!val1 && !val2) + { + /* update k, kp params */ + + kp += (2 * UQ_GR); + + if (kp > KPMAX) + kp = KPMAX; + + k = kp >> LSGR; + } + + if (val1 & 1) + mag = ((INT16)((val1 + 1) >> 1)) * -1; + else + mag = (INT16)(val1 >> 1); + + if ((pOutput - pDstData) < DstSize) + { + *pOutput = mag; + pOutput++; + } + + if (val2 & 1) + mag = ((INT16)((val2 + 1) >> 1)) * -1; + else + mag = (INT16)(val2 >> 1); + + if ((pOutput - pDstData) < DstSize) + { + *pOutput = mag; + pOutput++; + } + } + } + } + + offset = (pOutput - pDstData); + + if (offset < rDstSize) + { + size = DstSize - offset; + ZeroMemory(pOutput, size * 2); + pOutput += size; + } + + offset = (pOutput - pDstData); + + if (offset != DstSize) + return -1; + + return 1; +} + +/* Returns the next coefficient (a signed int) to encode, from the input stream */ +#define GetNextInput(_n) \ + do \ + { \ + if (data_size > 0) \ + { \ + _n = *data++; \ + data_size--; \ + } \ + else \ + { \ + _n = 0; \ + } \ + } while (0) + +/* Emit bitPattern to the output bitstream */ +#define OutputBits(numBits, bitPattern) rfx_bitstream_put_bits(bs, bitPattern, numBits) + +/* Emit a bit (0 or 1), count number of times, to the output bitstream */ +#define OutputBit(count, bit) \ + do \ + { \ + UINT16 _b = (bit ? 0xFFFF : 0); \ + int _c = (count); \ + for (; _c > 0; _c -= 16) \ + rfx_bitstream_put_bits(bs, _b, (_c > 16 ? 16 : _c)); \ + } while (0) + +/* Converts the input value to (2 * abs(input) - sign(input)), where sign(input) = (input < 0 ? 1 : + * 0) and returns it */ +#define Get2MagSign(input) ((input) >= 0 ? 2 * (input) : -2 * (input)-1) + +/* Outputs the Golomb/Rice encoding of a non-negative integer */ +#define CodeGR(krp, val) rfx_rlgr_code_gr(bs, krp, val) + +static void rfx_rlgr_code_gr(RFX_BITSTREAM* bs, int* krp, UINT32 val) +{ + int kr = *krp >> LSGR; + + /* unary part of GR code */ + + UINT32 vk = (val) >> kr; + OutputBit(vk, 1); + OutputBit(1, 0); + + /* remainder part of GR code, if needed */ + if (kr) + { + OutputBits(kr, val & ((1 << kr) - 1)); + } + + /* update krp, only if it is not equal to 1 */ + if (vk == 0) + { + UpdateParam(*krp, -2, kr); + } + else if (vk > 1) + { + UpdateParam(*krp, vk, kr); + } +} + +int rfx_rlgr_encode(RLGR_MODE mode, const INT16* WINPR_RESTRICT data, UINT32 data_size, + BYTE* WINPR_RESTRICT buffer, UINT32 buffer_size) +{ + int k = 0; + int kp = 0; + int krp = 0; + RFX_BITSTREAM* bs = NULL; + int processed_size = 0; + + if (!(bs = (RFX_BITSTREAM*)winpr_aligned_calloc(1, sizeof(RFX_BITSTREAM), 32))) + return 0; + + rfx_bitstream_attach(bs, buffer, buffer_size); + + /* initialize the parameters */ + k = 1; + kp = 1 << LSGR; + krp = 1 << LSGR; + + /* process all the input coefficients */ + while (data_size > 0) + { + int input = 0; + + if (k) + { + int numZeros = 0; + int runmax = 0; + int sign = 0; + + /* RUN-LENGTH MODE */ + + /* collect the run of zeros in the input stream */ + numZeros = 0; + GetNextInput(input); + while (input == 0 && data_size > 0) + { + numZeros++; + GetNextInput(input); + } + + // emit output zeros + runmax = 1 << k; + while (numZeros >= runmax) + { + OutputBit(1, 0); /* output a zero bit */ + numZeros -= runmax; + UpdateParam(kp, UP_GR, k); /* update kp, k */ + runmax = 1 << k; + } + + /* output a 1 to terminate runs */ + OutputBit(1, 1); + + /* output the remaining run length using k bits */ + OutputBits(k, numZeros); + + /* note: when we reach here and the last byte being encoded is 0, we still + need to output the last two bits, otherwise mstsc will crash */ + + /* encode the nonzero value using GR coding */ + const UINT32 mag = + (UINT32)(input < 0 ? -input : input); /* absolute value of input coefficient */ + sign = (input < 0 ? 1 : 0); /* sign of input coefficient */ + + OutputBit(1, sign); /* output the sign bit */ + CodeGR(&krp, mag ? mag - 1 : 0); /* output GR code for (mag - 1) */ + + UpdateParam(kp, -DN_GR, k); + } + else + { + /* GOLOMB-RICE MODE */ + + if (mode == RLGR1) + { + UINT32 twoMs = 0; + + /* RLGR1 variant */ + + /* convert input to (2*magnitude - sign), encode using GR code */ + GetNextInput(input); + twoMs = Get2MagSign(input); + CodeGR(&krp, twoMs); + + /* update k, kp */ + /* NOTE: as of Aug 2011, the algorithm is still wrongly documented + and the update direction is reversed */ + if (twoMs) + { + UpdateParam(kp, -DQ_GR, k); + } + else + { + UpdateParam(kp, UQ_GR, k); + } + } + else /* mode == RLGR3 */ + { + UINT32 twoMs1 = 0; + UINT32 twoMs2 = 0; + UINT32 sum2Ms = 0; + UINT32 nIdx = 0; + + /* RLGR3 variant */ + + /* convert the next two input values to (2*magnitude - sign) and */ + /* encode their sum using GR code */ + + GetNextInput(input); + twoMs1 = Get2MagSign(input); + GetNextInput(input); + twoMs2 = Get2MagSign(input); + sum2Ms = twoMs1 + twoMs2; + + CodeGR(&krp, sum2Ms); + + /* encode binary representation of the first input (twoMs1). */ + GetMinBits(sum2Ms, nIdx); + OutputBits(nIdx, twoMs1); + + /* update k,kp for the two input values */ + + if (twoMs1 && twoMs2) + { + UpdateParam(kp, -2 * DQ_GR, k); + } + else if (!twoMs1 && !twoMs2) + { + UpdateParam(kp, 2 * UQ_GR, k); + } + } + } + } + + rfx_bitstream_flush(bs); + processed_size = rfx_bitstream_get_processed_bytes(bs); + winpr_aligned_free(bs); + + return processed_size; +} |