Skip to content
  • Amy spark's avatar
    Feature: HDR gradients · 4cfda40a
    Amy spark authored
    This commit adds suport in Krita for rendering of HDR, high-bit-depth
    gradients, and dithering for all output bit depths.
    
    Firstly, all gradient operations are now upgraded; this includes color
    mixing ops (step values, upgraded to 16-bit signed integer), and
    gradients' default bit depth, which was upgraded to 16-bit integer
    or the image's bit depth, whichever is higher.
    
    Secondly, this commit implements a fully SSE+ vectorizable dithering
    operator, based on the Pixman low-level graphics library's
    implementation (MIT-licensed) available at the following commits:
    
    https://gitlab.freedesktop.org/pixman/pixman/-/commit/ddcc41b999562efdd9f88daa51ffbf39782748b5
    https://gitlab.freedesktop.org/pixman/pixman/-/commit/98b5ec74ca14448349ef6a33a663ad19d446ed6b
    https://gitlab.freedesktop.org/pixman/pixman/-/commit/cb2ec4268fbde0df3b588ce5cbe2e43e0465452
    
    This version follows closely the original paper of Ulichney's:
    
    Robert A. Ulichney. "Void-and-cluster method for dither array
    generation", Proc. SPIE 1913, Human Vision, Visual Processing, and
    Digital Display IV, (8 September 1993); doi:10.1117/12.152707
    
    Based on Pixman's work, there are two available options
    (but three in code, see below):
    
    - a no-op downsampler
    
    - blue-noise Ulichney's dithering, 64x64, which is the default when
    enabled
    
    - Bayer's ordered dithering, 8x8, which has been left as a backup.
    
    The dither operator works by upcasting the pixel to normalized,
    floating point color space; applying modulated noise with scale
    1 / 2^bit_depth, and then downcasting the pixel to the destination
    depth. For obvious reasons, the first step is skipped if the source
    is already floating point, and the two latter are no-ops if the
    destination's floating point.
    
    The implementation in this commit is structured in a two-level
    abstract pattern:
    
    - KisDitherOp is the interface that all operators
    expose to dither a single pixel. They are implemented as instances of
    KisDitherOpImpl<srcCSTraits, dstCSTraits, DitherType>.
    
    - KisDitherOpImpl is the template class that does the hard work
    described above.
    
    Instances of the dither operator are inserted on each colorspace
    through a templated, inlined factory function,
    addStandardDitherOps<srcCSTraits>(KoColorSpace *). Given the
    source bit depth and all known possible destination depths,
    this template calls another templated function, addDitherOpsByDepth<
    srcCSTraits, dstCSTraits, DitherType>(KoColorSpace *). Each call
    to this function creates a KisDitherOpImpl instance corresponding
    to each triplet of bit depths and dither implementation, and inserts it
    in the given colorspace.
    
    Since this operator needs both source and destination
    colorspaces' traits, I check at compilation time that all known traits
    have been considered at op creation.
    
    The vectorization properties have been tested with Xcode 11 on macOS,
    dumping kritalcmsengine.so with objdump, and checking disassembly
    manually.
    
    There are two ways to use this dither operator, once a copy has been
    obtained:
    
    - Use dither(const quint8* src, quint8* dst, int x, int y) to dither
    a single pixel.
    
    - Use dither(const quint8* src, int srcRowStride, quint8* dst,
    int dstRowStride, int x, int y, int columns, int rows) to dither a
    whole tile at once. This is the pattern that's used in
    KisGradientPainter.
    
    Additionally to Pixman's implementation, and the optimizations already
    provided by KoColorSpaceTraits, an optimized version has been
    made for the case of pass-through dither and no downcasting, where a
    memcpy call suffices.
    
    Finally, this implementation has been made available as a checkbox in:
    
    - the Gradient tool
    
    - Layer / Layer Styles / Gradient overlay
    
    - Fill Layers / Gradient generator
    
    Support has also been included for Photoshop's dithered gradients,
    when importing and exporting PSDs. As with the rest of Krita,
    they map to blue-noise dithering.
    
    Thanks to Wolthera van Hövell, Dmitry Kazakov, and Mathias Wein for
    their review.
    
    BUG: 343864
    CCMAIL: kimageshop@kde.org
    4cfda40a