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://cantwell-tom.medium.com/pixel-perfect-lines-in-html-canvas-112fc638d1e9

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

8 Likes

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

2 Likes

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).

6 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.

3 Likes

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()


8 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. ^.^

Hello, I’ll be very interested in this feature too, I agree it would make Krita an excellent pixel art software. With pixel perfect brushes and shapes, Krita would ally the power of its general functionalities (like file layers, or python scripting) to good pixel art tools and become a tool of choice for game devs.

Was the pixel perfect brush functionality officially requested ? Where to follow its development ? Thanks !

3 Likes

Hi @Elis4 - You can add a vote to this feature request if you like. It doesn’t mean the feature will actually get added but it does give the dev team a sense for how much interest there is.

5 Likes

It’s definitely a key element for Pixel Art, and I think would make Krita more usable for it. At the moment I am writing png file, doing the Pixel Perefct lines in Aseprite and bringing it back in again.

Recently I started doing pixel art and this would be a fantastic feature. I think making it a smooth option on the stabilizer would be the most sensible implementation of this.

This is not only useful for pixel art but for animation too, where a common workflow is to draw first with an aliased brush and then smooth it in post-processing.

This is great; I refactored the iteration process to only work on a selection and to ignore transparent pixels, so it is faster. Also, I added a thinning process so the end result is more akin to 1 pixel width lines. If anyone knows an L-shape or curve detection algorithm, post it, and maybe I can implement it on this.

script: pseudo-pixel-perfect.py Ā· GitHub

This is a demo of the tool working:

2 Likes

You guys are so badass omg :sob::sob::two_hearts:

The way ive been doing it (since krita on android does not have scripts) is by using layer styles stroke 1px. it’s only for closed shapes but good enough for blocking before i clean it up manually:


5 Likes

What! I didn’t know about that! Thanks for sharing it.

Amazing! Well done. Downloaded. It’s an awesome timesaver.

i really hope this gets added as a brush or brush option (togglable) I’m not very familiar with how to navigate github. Does anyone know if this is being worked on or if there is a proposal out there for it? I was originally going to come here to post about adding this feature but it seems like I was beat to the punch two years ago. Anyway here’s the one gif That I made for my post
bad

I was reading On the documentation for submitting a future request. Since I’m assuming that this has been already talked about I guess I’ll add why I think this would be an extremely helpful feature. When needing to Add line work, on a second layer I need to go ahead and draw over my sketch lines. This feature would be extremely helpful in the fact that I wouldn’t have to do the extra step of going back and erasing which jagged edge I don’t want. Big time saver. But I’m assuming if you’re already here, you already know the benefits.

See the recent announcement above!

1 Like

Hey all! I’m working on this feature now, trying to see how the users want this option implemented. I’m thinking about whether to add a checkbox inside sharpness (brush editor) and limit the feature to brushes with sharpness enabled or flat-out create a new tab ā€œpixel artā€ with the pixel-perfect option ( and maybe moving the ā€œAlign the brush preview outline to the pixel gridā€ there as well ). There will be a new brush preset with this option ticked regardless for easy access.

9 Likes

Ken Lo, You are fantastic and you’re making this transition for pixel art way more smoother. I’m still extremely new to Krita so I don’t know where everything should properly be as far as development goes but my original thought was I would go to tool options after selecting the pixel art brush and there would be a check box there but then I noticed that under brushes the tool options don’t change so I guess it wouldn’t make sense to put it there. I guess a pixel art pixel-perfect brush would do the trick as well. thank you so much!

5 Likes

Does Pixel perfect mean anything on anything other than a single pixel brush?

It continues to amaze me how poor circles and ovals come out with single pixel brush. This kind of ties in with the same topic.
ovals

Aseprite:
ovals

3 Likes

Awesome! I can see it working as a checkbox beneath the ā€˜Align the brush preview outline to the pixel grid’ option in the ā€˜Sharpness’ tab, but I don’t think it will be very discoverable to the intended users (both old and new) of the tool. Since the tool is mainly focused on pixel art artists, I think having the checkbox on the ā€˜Brush Tip’ tab under the ā€˜Anti-alias’ checkbox would be more discoverable and intuitive. Also, a brief tooltip would help the users.

2 Likes

I was looking for a setting for this today, while I was doing my first pixel art drawing in Krita.

Edit: removed my previous comment. I just saw @7th ’s post and completely agree. A new brush preset for this mode would be great! Thank you for your work on this @Ken_Lo !

1 Like