QOI: QOI_OP_RUN does not update the previously seen pixels, corrupting qoi_test_images/edgecase.qoi
Maintainer thường phản hồi trong vòng 1 ngày
@manit2004 đang làm issue này rồi.
Từ ngày 7/11/2026.
Đánh giá
- Độ khó
- 2/5
- Thời gian dự kiến
- 1-3 giờ
- Mức phù hợp với người mới
- 78/100
Hướng nghiên cứu
Đọc nhánh QOI_OP_RUN của QoiDecoder.decode() và lệnh gọi _add_to_previous_pixels() được mô tả trong issue. Tái hiện lỗi bằng ví dụ 29 byte được cung cấp, sau đó kiểm tra các bài kiểm thử của bộ giải mã QOI, bao gồm test_op_index. Công việc hoàn tất khi pixel run ban đầu có thể được QOI_OP_INDEX về sau sử dụng và đầu ra chính thức của edgecase.qoi khớp với edgecase.png.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
What did you do?
Opened a QOI image that starts with a run of the initial pixel (0, 0, 0, 255) and later refers to that pixel with QOI_OP_INDEX.
The official test image edgecase.qoi from https://qoiformat.org/qoi_test_images.zip does this. The script below is a minimal 29-byte reproduction.
What did you expect to happen?
The third pixel should be (0, 0, 0, 255), and edgecase.qoi should match edgecase.png.
The specification says "Each pixel that is seen by the encoder and decoder is put into this array". The reference decoder updates the array after every chunk, including QOI_OP_RUN.
What actually happened?
The third pixel is (0, 0, 0, 0):
[(0, 0, 0, 255), (1, 2, 3, 4), (0, 0, 0, 0)]
For edgecase.qoi, 10272 of the 16384 pixels differ from edgecase.png.
The QOI_OP_RUN branch in QoiDecoder.decode() reaches continue before _add_to_previous_pixels() is called, so the pixel is never added to the array. For most pixels this does no harm, because the previous pixel is already in the array from the chunk that produced it. The initial pixel is the exception, because no chunk produced it.
This looks like a regression from #9008, first released in 11.3.0. That PR correctly stopped adding the initial pixel to the array up front, but the up-front add had been hiding this problem. I did not run older releases. On 13.0.0, restoring the removed line makes both the script below and edgecase.qoi decode correctly.
qoi.h's encoder does not add run pixels to its array, so it does not produce this pattern. Encoders that follow the specification literally can, as edgecase.qoi shows.
This change fixes it:
value = self._previous_pixel
+ self._add_to_previous_pixels(value)
if bands == 3:
With this change, all eight official test images match their PNGs, and QOI_OP_INDEX as the first chunk still gives (0, 0, 0, 0), as test_op_index expects. I have not run the full test suite.
What are your OS, Python and Pillow versions?
- OS: Fedora Linux 43
- Python: 3.14.8
- Pillow: 13.0.0. The
QoiDecodercode onmainis identical.
--------------------------------------------------------------------
Pillow 13.0.0
Python 3.14.8 (main, Oct 1 2026, 00:00:00) [GCC 15.3.1 20260722 (Red Hat 15.3.1-1)]
--------------------------------------------------------------------
Python executable is <venv>/bin/python
Environment Python files loaded from <venv>
System Python files loaded from /usr
--------------------------------------------------------------------
Python Pillow modules loaded from <venv>/lib64/python3.14/site-packages/PIL
Binary Pillow modules loaded from <venv>/lib64/python3.14/site-packages/PIL
--------------------------------------------------------------------
--- PIL CORE support ok, compiled for 13.0.0
--- TKINTER support ok, loaded 9.0
--- 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.1.zlib-ng, 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.16, harfbuzz 14.5.0
*** LIBIMAGEQUANT (Quantization method) support not installed
--- XCB (X protocol) support ok
--------------------------------------------------------------------
from io import BytesIO
from PIL import Image
data = (
b"qoif"
+ (3).to_bytes(4, "big") # width
+ (1).to_bytes(4, "big") # height
+ bytes([4, 0]) # channels, colorspace
+ bytes([0xC0]) # QOI_OP_RUN, length 1: repeats the initial (0, 0, 0, 255)
+ bytes([0xFF, 1, 2, 3, 4]) # QOI_OP_RGBA
+ bytes([53]) # QOI_OP_INDEX: 255 * 11 % 64 == 53, the slot of (0, 0, 0, 255)
+ bytes(7)
+ b"\x01" # end marker
)
with Image.open(BytesIO(data)) as im:
print([im.getpixel((x, 0)) for x in range(3)])
- Ngôn ngữ chính
- Python
- Star
- 14k
- Fork
- 2.6k
- Merge trung bình
- 1 ngày 4 giờ
- Pull request đã merge (30 ngày)
- 182
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của python-pillow/Pillow
-
Converting F images to I turns large values, inf and NaN into -2147483648Có thể đã có người làm Có pull request liên kết đang mở hoặc đã được merge. Đang mởConversion
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
python-pillow/Pillow#10372 · 5 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Sun raster files with RLE encoding and odd-width rows decode shiftedCó thể đã có người làm @PerryLink đã nhận 6 ngày trước. Đang mở🤖-assisted
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
python-pillow/Pillow#10370 · 3 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Warn that pre-applying arabic_reshaper + python-bidi corrupts text on a Raqm buildCó thể đã có người làm @Syamjith-NK đã nhận 70 ngày trước. Đang mởDocumentation Font Rendering PNG Third Party Framework
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
python-pillow/Pillow#9907 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
🤖-assisted
Độ khó 4/5 1-2 ngày Mức phù hợp với người mới 55/100
python-pillow/Pillow#10438 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
`ImageDraw.arc()` segfaults on a tuple subclass whose `__len__` is too largeCó thể đã có người làm @Parsiffall1 đã nhận 9 ngày trước. Đang mở
Độ khó 4/5 1-2 ngày Mức phù hợp với người mới 58/100
python-pillow/Pillow#10358 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của python-pillow/Pillow
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 74/100
-
enhancement
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 66/100
RunestoneInteractive/rs#1661 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 64/100
streamlink/streamlink#7192 · 4 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 1/5 1-3 giờ Mức phù hợp với người mới 75/100
Maintainer thường phản hồi trong vòng 2 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
aws-samples/sample-per-model-bedrock#795 ·
Maintainer thường phản hồi trong vòng 1 ngày