aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/Pillow/py3/libImaging/SunRleDecode.c
blob: 9d8e1292a47add7c911c9f0b60eb5a77d48981f1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*
 * THIS IS WORK IN PROGRESS
 *
 * The Python Imaging Library.
 * $Id$
 *
 * decoder for SUN RLE data.
 *
 * history:
 *      97-01-04 fl     Created
 *
 * Copyright (c) Fredrik Lundh 1997.
 * Copyright (c) Secret Labs AB 1997.
 *
 * See the README file for information on usage and redistribution.
 */

#include "Imaging.h"

int
ImagingSunRleDecode(Imaging im, ImagingCodecState state, UINT8 *buf, Py_ssize_t bytes) {
    int n;
    UINT8 *ptr;
    UINT8 extra_data = 0;
    UINT8 extra_bytes = 0;

    ptr = buf;

    for (;;) {
        if (bytes < 1) {
            return ptr - buf;
        }

        if (ptr[0] == 0x80) {
            if (bytes < 2) {
                break;
            }

            n = ptr[1];

            if (n == 0) {
                /* Literal 0x80 (2 bytes) */
                n = 1;

                state->buffer[state->x] = 0x80;

                ptr += 2;
                bytes -= 2;

            } else {
                /* Run (3 bytes) */
                if (bytes < 3) {
                    break;
                }

                /* from (https://www.fileformat.info/format/sunraster/egff.htm)

                   For example, a run of 100 pixels with the value of
                   0Ah would encode as the values 80h 64h 0Ah. A
                   single pixel value of 80h would encode as the
                   values 80h 00h. The four unencoded bytes 12345678h
                   would be stored in the RLE stream as 12h 34h 56h
                   78h.  100 pixels, n=100, not 100 pixels, n=99.

                   But Wait! There's More!
                   (https://www.fileformat.info/format/sunraster/spec/598a59c4fac64c52897585d390d86360/view.htm)

                   If the first byte is 0x80, and the second byte is
                   not zero, the record is three bytes long. The
                   second byte is a count and the third byte is a
                   value. Output (count+1) pixels of that value.

                   2 specs, same site, but Imagemagick and GIMP seem
                   to agree on the second one.
                */
                n += 1;

                if (state->x + n > state->bytes) {
                    extra_bytes = n; /* full value */
                    n = state->bytes - state->x;
                    extra_bytes -= n;
                    extra_data = ptr[2];
                }

                memset(state->buffer + state->x, ptr[2], n);

                ptr += 3;
                bytes -= 3;
            }

        } else {
            /* Literal byte */
            n = 1;

            state->buffer[state->x] = ptr[0];

            ptr += 1;
            bytes -= 1;
        }

        for (;;) {
            state->x += n;

            if (state->x >= state->bytes) {
                /* Got a full line, unpack it */
                state->shuffle(
                    (UINT8 *)im->image[state->y + state->yoff] +
                        state->xoff * im->pixelsize,
                    state->buffer,
                    state->xsize);

                state->x = 0;

                if (++state->y >= state->ysize) {
                    /* End of file (errcode = 0) */
                    return -1;
                }
            }

            if (extra_bytes == 0) {
                break;
            }

            if (state->x > 0) {
                break;  // assert
            }

            if (extra_bytes >= state->bytes) {
                n = state->bytes;
            } else {
                n = extra_bytes;
            }
            memset(state->buffer + state->x, extra_data, n);
            extra_bytes -= n;
        }
    }

    return ptr - buf;
}