Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Sun raster files with RLE encoding and odd-width rows decode shifted

Open Beginner friendly
#10,370 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
72/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
python
Domain
tooling

Research direction

Start with the Image.open() reproduction script and locate the sun_rle decoder that handles type 2 Sun raster data. Trace how the stored row stride is calculated and compare the RLE path with the raw path, which already receives the padded stride. The work is done when odd-width RLE images decode each row without including padding bytes, with a regression test covering the reported 3x2 grayscale case.

Written by the indexing model from the issue text.

Description

What did you do?

I opened a run-length-encoded (type 2) Sun raster image whose rows hold an odd number of bytes (an 8-bit grayscale image 3 pixels wide) using Image.open() on an in-memory file created by the self-contained script below.

In this format every stored row is padded to an even number of bytes, and the RLE stream encodes the padding bytes too (SUN_RAS.TXT: "Regardless of width, the stored scanlines are rounded up to multiples of 16 bits", "A run is not terminated at the end of a scan line").

What did you expect to happen?
row 0: 16 32 48
row 1: 64 80 96
What actually happened?

The padding byte at the end of row 0 was decoded as the first pixel of row 1, and every following row is shifted one more byte, without any error:

row 0: 16 32 48
row 1: 0 64 80

Any image whose pixels take an odd number of bytes per row is affected (8-bit and 24-bit images of odd width; 1-bit images 1–8, 17–24, … pixels wide; 4-bit images 1–2, 5–6, … pixels wide). Images with even-length rows decode correctly as well as uncompressed (type 1) Sun raster files.

What are your OS, Python and Pillow versions?
  • OS: Ubuntu 24.04, x86_64
  • Python: 3.12.3
  • Pillow: 13.0.0; also reproduced with 12.3.0 and current main (13.1.0.dev0, 1c5bdeb2c)
--------------------------------------------------------------------
Pillow 13.0.0
Python 3.12.3 (main, Aug 31 2026, 10:18:26) [GCC 13.3.0]
--------------------------------------------------------------------
--- PIL CORE support ok, compiled for 13.0.0
*** TKINTER support not installed
--- FREETYPE2 support ok, loaded 2.14.3
--- LITTLECMS2 support ok, loaded 2.19
--- WEBP support ok, loaded 1.6.0
--- AVIF support ok, loaded 1.4.2
--- JPEG support ok, compiled for libjpeg-turbo 3.2.0
--- OPENJPEG (JPEG2000) support ok, loaded 2.5.4
--- ZLIB (PNG/ZIP) support ok, loaded 1.3, compiled for zlib-ng 2.3.3
--- LIBTIFF support ok, loaded 4.7.2
--- RAQM (Bidirectional Text) support ok, loaded 0.11.0, fribidi 1.0.13, harfbuzz 14.5.0
*** LIBIMAGEQUANT (Quantization method) support not installed
--- XCB (X protocol) support ok
--------------------------------------------------------------------
import io
import struct

from PIL import Image

# 8-bit grayscale, 3x2 pixels, type 2 (run-length encoded).
# Each stored row is 3 pixel bytes + 1 padding byte.
header = struct.pack(">8I", 0x59A66A95, 3, 2, 8, 8, 2, 0, 0)
data = bytes([0x10, 0x20, 0x30, 0x00,  # row 0: pixels 16 32 48, padding
              0x40, 0x50, 0x60, 0x00])  # row 1: pixels 64 80 96, padding

with Image.open(io.BytesIO(header + data)) as im:
    rows = [[im.getpixel((x, y)) for x in range(3)] for y in range(2)]

print("decoded :", rows)
print("expected:", [[0x10, 0x20, 0x30], [0x40, 0x50, 0x60]])

Output:

decoded : [[16, 32, 48], [0, 64, 80]]
expected: [[16, 32, 48], [64, 80, 96]]

Passing the padded stride to the sun_rle decoder (the same stride the raw path already computes) fixes it in my testing — happy to send a PR.

Found by genproof.com Formal Verification Agent. Reviewed and approved by a human.

Dominant language
Python
Stars
13.9k
Forks
2.6k
Avg merge
1d 2h
Merged PRs (30d)
176

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from python-pillow/Pillow

All issues in python-pillow/Pillow

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.