Bug 450503

Summary: ThumbCreator: QImage with bytesPerLine > width*4 is rendered incorrectly
Product: [Frameworks and Libraries] kio-extras Reporter: David Korth <gerbilsoft>
Component: Thumbnails and previewsAssignee: Plasma Bugs List <plasma-bugs-null>
Status: REPORTED ---    
Severity: normal    
Priority: NOR    
Version First Reported In: 21.12.2   
Target Milestone: ---   
Platform: Other   
OS: Linux   
Latest Commit: Version Fixed/Implemented In:
Sentry Crash Report:

Description David Korth 2022-02-18 07:16:19 UTC
I have a multi-platform shell extension called "rom-properties". It uses some custom memory management for performance; namely, it uses 16-byte aligned memory buffers for QImage to allow usage of SSE2 and SSSE3-optimized algorithms.

I recently encountered an image from the Khronox KTX test suite, hi_mark_sq.ktx, that's 145x130 24-bit color. When decoded to 32-bit color, it's 580 bytes per row if not using custom row alignment; however, with 16-byte alignment, it's 592 bytes per row. QImage handles this perfectly fine, but once I hand it off to KIO in my ThumbCreator::create() function, it gets distorted. I narrowed this down to the SHM code in kio/src/widgets/previewjob.cpp and kio-extras/thumbnail/thumbnail.cpp, which does not account for bytesPerLine differing from width * bytes per pixel.

This is likely an edge case, but at the very least, some documentation should be added to ThumbCreator to indicate that this would be distorted.

I did add a workaround to my own code to create a copy of the QImage without the extra row bytes. kio-extras has a similar workaround for paletted images by converting it to ARGB32, so maybe this workaround can be added here.

https://invent.kde.org/network/kio-extras/-/blob/master/thumbnail/thumbnail.cpp#L318

Something like:

if (img.format() != QImage::Format_ARGB32) { // KIO::PreviewJob and this code below completely ignores colortable :-/,
    img = img.convertToFormat(QImage::Format_ARGB32); //  so make sure there is none
} else if (img.width() * 4 != img.bytesPerLine()) {
    img = img.copy();
}