Pixel perfect line setting for pixel art brushes

I use Krita as my main tool for creating illustration, textures, sprites, animation, etc; but I still feel it could improve in the field f game sprites with an option like pixel perfect line. Of course it can be done manually, but this takes a lot of time and effort that could be saved with this feature.

The explanation of this feature can be easily seen in this image:

image

In the left side you can see how actually krita draw 1 pixel lines, an the seccond one is how pixel perfect looks. Pixel perfect is a way to visually smooth pixel art. It consist in making the pixels in a line with a progression of consecutive pixels in the same axis, so the curves could look more natural and less broken. It is really useful for pixel art creation, but also for fav icons, correct images that are downscaled, control small details, etc.

I’m not developer, so I can’t help much with the feature itself, but knowing about the way it works, I found this two refs that could give you an idea of how other people solved it for their own software.

https://rickyhan.com/jekyll/update/2018/11/22/pixel-art-algorithm-pixel-perfect.html

Hope this helps to describe the idea and someone figure how to make Krita even more amazing with this feature.

3 Likes

This is an other example of how Aseprite solved it in v 0.9:

1 Like

The same commit on LibreSprite: Add pixel-perfect drawing mode for freehand tools (issue 315) · LibreSprite/LibreSprite@1554875 · GitHub
LibreSprite contains all the GPL-licensed Aseprite code. Using LibreSprite as a source ensures that the code is GPL so we can use it :slight_smile:

We have a GSOC this year about bringing in pixel-perfect ellipses, I’m afraid that adding pixel-perfect lines too might be too much for the time requirement, but it would be good stretch goal (if the student has finished the project and still has more time).

2 Likes

It would be really amazing, and would give Krita even more value in desktop and mobile. Mixing the animation features with pixel perfect options for brushes, it would be really a complete suite for people who work in game industry.

1 Like

I wrote the following script for my personal need.
(This is not a pixel_perfect feature and code not cleaned up)
also I do not really understand bytearray part. so sorry if there are any mistakes.
If anyone is interested, please try it.

(usage)
copy all code → save it in any folder you want. named (e.g.) removedoubles.py
→ enable ten script plugin → assign above script → drawing
→ press the specified shortcut key

I tried LibreSprite but I still like Krita
and I hope pixel perfect feature will be officially implemented someday.
sorry for my bad English.

from krita import *
from PyQt5.QtCore import QByteArray

doc = Krita.instance().activeDocument()
currentLayer = doc.activeNode()

def remove_doubles():
    byte_ = QByteArray(b'\x00\x00\x00\x00')#transparent
    black_byte_ = QByteArray(b'\x00\x00\x00\xff')#black
    t = b'\x00\x00\x00\x00'
    b = b'\x00\x00\x00\xff'
    for i in range(doc.width()):
        for j in range(doc.height()):
            r = currentLayer.pixelData(i+1, j+0, 1,1)
            l = currentLayer.pixelData(i-1, j+0, 1,1)
            u = currentLayer.pixelData(i+0, j-1, 1,1)
            d = currentLayer.pixelData(i+0, j+1, 1,1)
            ru = currentLayer.pixelData(i+1, j-1, 1,1)
            rd = currentLayer.pixelData(i+1, j+1, 1,1)
            lu = currentLayer.pixelData(i-1, j-1, 1,1)
            ld = currentLayer.pixelData(i-1, j+1, 1,1)
            
            if r == t and \
                l == t and \
                u == t and \
                d == t and \
                ru == t and \
                rd == t and \
                lu == t and \
                ld == t:
                    currentLayer.setPixelData(byte_, i, j, 1, 1)
                    
            elif r == b and \
                l == t and \
                u == t and \
                d == b and \
                ru == t and \
                rd == t and \
                lu == t and \
                ld == t:
                    currentLayer.setPixelData(byte_, i, j, 1, 1)
            elif l == b and \
                r == t and \
                u == t and \
                d == b and \
                ru == t and \
                rd == t and \
                lu == t and \
                ld == t:
                    currentLayer.setPixelData(byte_, i, j, 1, 1)
                    
            elif r == b and \
                l == t and \
                d == t and \
                u == b and \
                ru == t and \
                rd == t and \
                lu == t and \
                ld == t:
                    currentLayer.setPixelData(byte_, i, j, 1, 1)
            elif l == b and \
                r == t and \
                d == t and \
                u == b and \
                ru == t and \
                rd == t and \
                lu == t and \
                ld == t:
                    currentLayer.setPixelData(byte_, i, j, 1, 1)

            elif l == b and \
                r == t and \
                d == t and \
                u == b and \
                ru == b and \
                rd == t and \
                lu == t and \
                ld == b:
                    currentLayer.setPixelData(byte_, i, j, 1, 1)
            elif l == b and \
                r == t and \
                u == t and \
                d == b and \
                rd == b and \
                ru == t and \
                ld == t and \
                lu == b:
                    currentLayer.setPixelData(byte_, i, j, 1, 1)

            elif l == b and \
                r == t and \
                u == t and \
                d == b and \
                rd == b and \
                ru == t and \
                ld == t and \
                lu == t:
                    currentLayer.setPixelData(byte_, i, j, 1, 1)
            elif r == b and \
                l == t and \
                u == t and \
                d == b and \
                ru == b and \
                rd == t and \
                lu == t:
                    currentLayer.setPixelData(byte_, i, j, 1, 1)

            elif l == b and \
                r == t and \
                d == t and \
                u == b and \
                rd == t and \
                ru == b and \
                ld == t and \
                lu == t:
                    currentLayer.setPixelData(byte_, i, j, 1, 1)
            elif r == b and \
                l == t and \
                d == t and \
                u == b and \
                rd == t and \
                ru == t and \
                ld == t and \
                lu == b:
                    currentLayer.setPixelData(byte_, i, j, 1, 1)

            elif r == b and \
                l == t and \
                d == t and \
                u == b and \
                rd == b and \
                ru == t and \
                ld == t and \
                lu == t:
                    currentLayer.setPixelData(byte_, i, j, 1, 1)
            elif l == b and \
                r == t and \
                d == t and \
                u == b and \
                rd == t and \
                ru == t and \
                ld == b and \
                lu == t:
                    currentLayer.setPixelData(byte_, i, j, 1, 1)

        doc.refreshProjection()
    doc.waitForDone()
    QMessageBox.information(QWidget(), "", "completed")


remove_doubles()


3 Likes

This looks really promising, thank you for sharing!

I mostly use krita on android, so I can’t use scripts on there sadly, but I’ll try it out when I get on a desktop in the future. ^.^