Pillow/src/libImaging/TgaRleEncode.c

158 lines
4.3 KiB
C
Raw Normal View History

2018-06-15 23:01:06 +03:00
#include "Imaging.h"
#include <assert.h>
#include <string.h>
2021-01-03 06:17:51 +03:00
static int
comparePixels(const UINT8 *buf, int x, int bytesPerPixel) {
2018-06-15 23:01:06 +03:00
buf += x * bytesPerPixel;
return memcmp(buf, buf + bytesPerPixel, bytesPerPixel) == 0;
}
int
2021-01-03 06:17:51 +03:00
ImagingTgaRleEncode(Imaging im, ImagingCodecState state, UINT8 *buf, int bytes) {
UINT8 *dst;
2018-06-15 23:01:06 +03:00
int bytesPerPixel;
if (state->state == 0) {
if (state->ystep < 0) {
state->ystep = -1;
state->y = state->ysize - 1;
2020-05-10 12:56:36 +03:00
} else {
2018-06-15 23:01:06 +03:00
state->ystep = 1;
2020-05-10 12:56:36 +03:00
}
2018-06-15 23:01:06 +03:00
state->state = 1;
}
dst = buf;
bytesPerPixel = (state->bits + 7) / 8;
while (1) {
int flushCount;
/*
* state->count is the numbers of bytes in the packet,
* excluding the 1-byte descriptor.
*/
if (state->count == 0) {
2021-01-03 06:17:51 +03:00
UINT8 *row;
2018-06-15 23:01:06 +03:00
UINT8 descriptor;
int startX;
assert(state->x <= state->xsize);
/* Make sure we have space for the descriptor. */
2020-05-10 12:56:36 +03:00
if (bytes < 1) {
2018-06-15 23:01:06 +03:00
break;
2020-05-10 12:56:36 +03:00
}
2018-06-15 23:01:06 +03:00
if (state->x == state->xsize) {
state->x = 0;
state->y += state->ystep;
if (state->y < 0 || state->y >= state->ysize) {
state->errcode = IMAGING_CODEC_END;
break;
}
}
2020-05-10 12:56:36 +03:00
if (state->x == 0) {
2018-06-15 23:01:06 +03:00
state->shuffle(
state->buffer,
2021-01-03 06:17:51 +03:00
(UINT8 *)im->image[state->y + state->yoff] +
state->xoff * im->pixelsize,
2018-06-15 23:01:06 +03:00
state->xsize);
2020-05-10 12:56:36 +03:00
}
2018-06-15 23:01:06 +03:00
row = state->buffer;
/* Start with a raw packet for 1 px. */
descriptor = 0;
startX = state->x;
state->count = bytesPerPixel;
if (state->x + 1 < state->xsize) {
int maxLookup;
int isRaw;
isRaw = !comparePixels(row, state->x, bytesPerPixel);
++state->x;
/*
* A packet can contain up to 128 pixels;
* 2 are already behind (state->x points to
* the second one).
*/
maxLookup = state->x + 126;
/* A packet must not span multiple rows. */
2020-05-10 12:56:36 +03:00
if (maxLookup > state->xsize - 1) {
2018-06-15 23:01:06 +03:00
maxLookup = state->xsize - 1;
2020-05-10 12:56:36 +03:00
}
2018-06-15 23:01:06 +03:00
if (isRaw) {
2020-05-10 12:56:36 +03:00
while (state->x < maxLookup) {
if (!comparePixels(row, state->x, bytesPerPixel)) {
2018-06-15 23:01:06 +03:00
++state->x;
2020-05-10 12:56:36 +03:00
} else {
2018-06-15 23:01:06 +03:00
/* Two identical pixels will go to RLE packet. */
--state->x;
break;
}
2020-05-10 12:56:36 +03:00
}
2018-06-15 23:01:06 +03:00
state->count += (state->x - startX) * bytesPerPixel;
} else {
descriptor |= 0x80;
2020-05-10 12:56:36 +03:00
while (state->x < maxLookup) {
if (comparePixels(row, state->x, bytesPerPixel)) {
2018-06-15 23:01:06 +03:00
++state->x;
2020-05-10 12:56:36 +03:00
} else {
2018-06-15 23:01:06 +03:00
break;
2020-05-10 12:56:36 +03:00
}
}
2018-06-15 23:01:06 +03:00
}
}
/*
* state->x currently points to the last pixel to be
* included in the packet. The pixel count in the
* descriptor is 1 less than actual number of pixels in
* the packet, that is, state->x == startX if we encode
* only 1 pixel.
*/
descriptor += state->x - startX;
*dst++ = descriptor;
--bytes;
/* Advance to past-the-last encoded pixel. */
++state->x;
}
assert(bytes >= 0);
assert(state->count > 0);
assert(state->x > 0);
assert(state->count <= state->x * bytesPerPixel);
2020-05-10 12:56:36 +03:00
if (bytes == 0) {
2018-06-15 23:01:06 +03:00
break;
2020-05-10 12:56:36 +03:00
}
2018-06-15 23:01:06 +03:00
flushCount = state->count;
2020-05-10 12:56:36 +03:00
if (flushCount > bytes) {
2018-06-15 23:01:06 +03:00
flushCount = bytes;
2020-05-10 12:56:36 +03:00
}
2018-06-15 23:01:06 +03:00
memcpy(
2021-01-03 06:17:51 +03:00
dst, state->buffer + (state->x * bytesPerPixel - state->count), flushCount);
2018-06-15 23:01:06 +03:00
dst += flushCount;
bytes -= flushCount;
state->count -= flushCount;
}
return dst - buf;
}