syndilights/open-lighting-architecture/ola-0.8.4/common/utils/RunLengthEncoder.cpp

136 lines
4.1 KiB
C++

/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* RunLengthEncoder.cpp
* The Run Length Encoder
* Copyright (C) 2005-2009 Simon Newton
*/
#include <string.h>
#include <ola/RunLengthEncoder.h>
namespace ola {
/*
* Take a DMXBuffer and RunLengthEncode the data
* @param src the DmxBuffer with the DMX data
* @param data where to store the RLE data
* @param size the size of the data segment, set to the amount of data encoded
* @return true if we encoded all data, false if we ran out of space
*/
bool RunLengthEncoder::Encode(const DmxBuffer &src,
uint8_t *data,
unsigned int &data_size) {
unsigned int src_size = src.Size();
unsigned int dst_size = data_size;
unsigned int &dst_index = data_size;
dst_index = 0;
unsigned int i;
for (i = 0; i < src_size && dst_index < dst_size;) {
// j points to the first non-repeating value
unsigned int j = i + 1;
while (j < src_size && src.Get(i) == src.Get(j) && j - i < 0x7f) {
j++;
}
// if the number of repeats is more than 2
// don't encode only two repeats,
if (j - i > 2) {
// if room left in dst buffer
if (dst_size - dst_index > 1) {
data[dst_index++] = (REPEAT_FLAG | (j - i));
data[dst_index++] = src.Get(i);
} else {
// else return what we have done so far
return false;
}
i = j;
} else {
// this value doesn't repeat more than twice
// find out where the next repeat starts
// postcondition: j is one more than the last value we want to send
for (j = i + 1; j < src_size - 2 && j - i < 0x7f; j++) {
// at the end of the array
if (j == src_size - 2) {
j = src_size;
break;
}
// if we're found a repeat of 3 or more stop here
if (src.Get(j) == src.Get(j+1) && src.Get(j) == src.Get(j+2))
break;
}
if (j >= src_size - 2)
j = src_size;
// if we have enough room left for all the values
if (dst_index + j - i < dst_size) {
data[dst_index++] = j - i;
memcpy(&data[dst_index], src.GetRaw() + i, j-i);
dst_index += j - i;
i = j;
// see how much data we can get in
} else if (dst_size - dst_index > 1) {
unsigned int l = dst_size - dst_index -1;
data[dst_index++] = l;
memcpy(&data[dst_index], src.GetRaw() + i, l);
dst_index += l;
return false;
} else {
return false;
}
}
}
if (i < src_size)
return false;
else
return true;
}
/*
* Decode the RLE'ed data into a DmxBuffer.
* @param dst the DmxBuffer to store the result
* @param start_channel the first channel for the RLE'ed data
* @param src_data the data to decode
* @param length the length of the data to decode
*/
bool RunLengthEncoder::Decode(DmxBuffer *dst,
unsigned int start_channel,
const uint8_t *src_data,
unsigned int length) {
int destination_index = start_channel;
for (unsigned int i = 0; i < length;) {
unsigned int segment_length = src_data[i] & (~REPEAT_FLAG);
if (src_data[i] & REPEAT_FLAG) {
i++;
dst->SetRangeToValue(destination_index, src_data[i++], segment_length);
} else {
i++;
dst->SetRange(destination_index, src_data + i, segment_length);
i += segment_length;
}
destination_index += segment_length;
}
return true;
}
} // ola