Questions for an image multiplying script

Original Post

Hi! I’m making a script to export an image with “tiling” (really just 200% width, 200% height, or both) because I’m making scrolling looped images for my motion graphics project. My process without scripting so far has been:

  1. Paint a single instance of the image. (Krita’s wrap-around feature is perfect for this.)
  2. Export the image as a PNG to my project’s directory.
  3. Open the PNG from that directory, double the canvas (horizontally for horizontal scrolling, vertically for vertical scrolling, or both for diagonal scrolling), and then paste a copy of the image exactly adjacently to the first instance.
  4. Save the PNG.

Now I want to make a script that can perform those steps, running with the original .kra as the active document. I just have a couple of questions on writing this.

(My initial questions were just answered with 1. I need to check that a document is active, and 2. It’s not necessary to save a temporary PNG when I’m using a script.)

Update Post
I made a script that basically does the actions described above, except instead of saving the temporary PNG it generates a new Krita document with copies of the layers, and from there the user can export normally.

This took me a while to figure out because the way Krita sizes transparency masks isn’t intuitive (for this application at least); even if you crop the image, the mask could be a different size, which messes up the appearances of the duplications. I ended up just copying the original image over to the new document and then flattening those layers, and then duplicating that single layer. I don’t like having to use nodeByName(), but I haven’t found a better way to reference the image that after the merge.

### FogTech 2.0
### A Krita script to duplicate an image so that it can be used for a loop
### 7 September 2020

from PyQt5.QtWidgets import QDialog, QHBoxLayout, QPushButton

currentDocument = Krita.instance().activeDocument()

# moveGroup()
# Obsolete against my wishes; moving Krita's layer masks doesn't work well
# because of how differently they are sized relative to the canvas.
# Instead, I've opted to just merge the original image into a single layer
# (which has the masks applied) and then duplicate that.
def moveGroup(layerGroup, x, y):
    for i in layerGroup.childNodes():
        for j in i.childNodes(): # For mod layers such as masks.
            j.move(x, y)
        if i.type() == "paintlayer":
            i.move(x, y)

# makeDoubleImage()
# Function that performs the Krita calls.
def makeDoubleImage(choice):

    newWidth  = 0
    newHeight = 0
    newName  = ""

    if currentDocument != None:
        curr_root_node = currentDocument.rootNode()
        newWidth  = currentDocument.width()
        newHeight = currentDocument.height()
        newName   = currentDocument.name() + "_scrollable"
    else:
        print("Error: No document active. Ending function.")
        return

    if choice == 1:
        print("Horizontal Scroll")
        newWidth = newWidth * 2
        print("newWidth is now: ", newWidth)

    if choice == 2:
        print("Vertical Scroll")
        newHeight = newHeight * 2
        print("newHeight is now: ", newHeight)

    if choice == 3:
        print ("Diagonal Scroll")
        newWidth = newWidth * 2
        newHeight = newHeight * 2

        print("(newWidth, newHeight) are now ({}, {})".format(newWidth, newHeight))

    # Use the original canvas size so that the image can be trimmed to it before duplication.
    newDocument = Krita.instance().createDocument(currentDocument.width(), currentDocument.height(),\
newName, "RGBA", "U8", "", 300.0)


    # Make a new Krita document.
    new_root_node = newDocument.rootNode()
    group1 = newDocument.createGroupLayer("G1")
    new_root_node.addChildNode(group1, None)

    # A new Krita document comes with a "Background" layer,
    # which should be turned off.
    newDocument.nodeByName("Background").setVisible(False)


    # Copy over every visible layer.
    for i in curr_root_node.childNodes():
        if i.visible() == True:
            layerCopy = i.duplicate()
            group1.addChildNode(layerCopy, None)

     # Crop the document to the image's size to remove off-canvas pixels.
    newDocument.crop(0 ,0, currentDocument.width(), currentDocument.height())
    newDocument.flatten()

     # Now expand the canvas to the new size.
    newDocument.setWidth(newWidth)
    newDocument.setHeight(newHeight)


    # Duplicate the flattened image.
    instance2 = newDocument.nodeByName("root Merged").duplicate()
    instance2.setName("I2")
    if (instance2 == 0):
        print("Error: root Merged not found.")

    newDocument.rootNode().addChildNode(instance2, None)

    # Move the duplicate.
    # Create more duplicates if needed for a diagonal scroll.
    if choice == 1: # One horizontal copy.
        instance2.move(currentDocument.width(), 0)

    if choice == 2: # One vertical copy.
        instance2.move(0, currentDocument.height())

    if choice == 3: # 3 Copies: Horizontal, Vertical, and Diagonal.
        instance2.move(currentDocument.width(), 0)

        instance3 = newDocument.nodeByName("root Merged").duplicate()
        instance3.setName("I3")
        newDocument.rootNode().addChildNode(instance3, None)
        instance3.move(0, currentDocument.height())

        instance4 = newDocument.nodeByName("root Merged").duplicate()
        instance4.setName("I4")
        newDocument.rootNode().addChildNode(instance4, None)
        instance4.move(currentDocument.width(), currentDocument.height())

    newDocument.refreshProjection()
    Krita.instance().activeWindow().addView(newDocument) # shows it in the application

# Add buttons and layout for buttons.
layoutForButtons = QHBoxLayout()
choiceA = QPushButton("x2 Width - Horizontal Scroll")
choiceB = QPushButton("x2 Height - Vertical Scroll")
choiceC = QPushButton("x2 Both - Diagonal Scroll")
layoutForButtons.addWidget(choiceA)
layoutForButtons.addWidget(choiceB)
layoutForButtons.addWidget(choiceC)

# Connect the buttons.
choiceA.clicked.connect(lambda: makeDoubleImage(1))
choiceB.clicked.connect(lambda: makeDoubleImage(2))
choiceC.clicked.connect(lambda: makeDoubleImage(3))

# Create dialog and show it.
newDialog = QDialog()
newDialog.setLayout(layoutForButtons)
newDialog.setWindowTitle("Make a scrollable image!")
newDialog.exec_() # show the dialog

Would you mind posting the script code so that if anybody in future wants the same thing can refer to it?

I will do that.