I’m making a plugin that would allow me to export an image while ignoring layers that are tagged blue. If you’ve seen Clip Studio Paint’s draft layer feature, that’s what I’m trying to do. I followed the scripting tutorial to get a basic plugin on Krita that does a basic image export. Now I need to learn how to control which nodes are accepted for the doc.exportImage(fileName, InfoObject())
part. I’ve made scripts to do things like search through the layers and set layer properties before, but exportImage() is new to me, and I don’t see on the documentation from either exportImage() or InfoObject() how to pick the layers. Could someone direct me to the category of functions I need to research?
Hi
If it can help you, here InfoObject
I use for PNG/JPEG export:
color=QColor('#ff9966')
pngOptions=InfoObject()
pngOptions.setProperty('compression', 9) # 0 (no compression) to 9 (max compression)
pngOptions.setProperty('indexed', False) # True to use indexed palette
pngOptions.setProperty('interlaced', False) # True to use interlaced
pngOptions.setProperty('saveSRGBProfile', True) # False to not use sRGB profile
pngOptions.setProperty('forceSRGB', True) # False to not use convert to sRGB
pngOptions.setProperty('alpha', True) # False to not save alpha channel
pngOptions.setProperty('transparencyFillcolor', [color.red(), color.green(), color.blue()])
jpgOptions=InfoObject()
jpgOptions.setProperty('quality', 85) # 0 (high compression/low quality) to 100 (low compression/higher quality)
jpgOptions.setProperty('smoothing', 15) # 0 to 100
jpgOptions.setProperty('subsampling', 2) # 0=4:2:0 (smallest file size) 1=4:2:2 2=4:4:0 3=4:4:4 (Best quality)
jpgOptions.setProperty('progressive', True) # False for non pogressive JPEG file
jpgOptions.setProperty('optimize', True)
jpgOptions.setProperty('saveProfile', True) # False to not save icc profile
jpgOptions.setProperty('transparencyFillcolor', [color.red(), color.green(), color.blue()])
currentDocument=Krita.instance().activeDocument()
currentDocument.setBatchmode(True) # do not display export dialog box
saved = currentDocument.exportImage("test.png", pngOptions)
saved = currentDocument.exportImage("test.jpeg", jpgOptions)
Grum999
I found this tutorial:
This shows how to export select layers. So far, I’m thinking this could work:
- Make copies of all the layers that I want to export.
- Merge all those copies into a single layer.
- Export that layer (with whatever code needs to be added to retain the size of the whole document instead of cropping out transparent edges).
- Delete that layer because it’s temporary.
Is that basically how compositing works in Krita, or is there a better method? I guess if I put the copies into a group and then delete that group and its contents after the export, that would be properly defined.
If goal is to export a projection of all tagged blue layers in document, different methods can be applied for your case.
If you have list of files to process (mean: all .kra files already have BLUE tag properly set), the simplest is:
- Loop over documents
– Open document
– Loop over all layers
— If tagged blue, set visible ON
— Else set visible OFF
– Export document
– Close document without saving
If you want to be able to export current opened document, this solution may not be the best, because you may want to keep visible/hidden layers in their initial state after script has been executed.
In this case, there’s different solutions:
-
Use previous solution, but keep in a list initial visible/hidden state of layers, and then restore it after execution
-
Try to create a new document method:
- Save a copy of current document as a temporary file
- Open temporary document
– Loop over all layers
— If tagged blue, set visible ON
— Else set visible OFF
– Export document
– Close document without saving - Delete temporary document
- Try to create a new document method:
- Create a new document with same properties than current document
- Loop over all layers of source document
– If tagged blue, clone node and add cloned node to target document - Export document
- Close document without saving
Note: when looping over layers, it could be more or less complex according to your layer stacks
Example:
Root node
+- Group 1
| +- Layer 1.1 (blue)
| +- Layer 1.2
+- Group 2 (blue)
| +- Layer 2.1
| +- Layer 2.2
+- Group 3 (blue)
| +- Layer 3.1
| +- Group 3.2
| +- Layer 3.2.1 (blue)
+- Layer 4 (blue)
+- Layer 5
You’ll have to think what you want in final result
Basically:
- Group 1 is not blue, but contains blue layers…
Do you have to export layer Group 1 or not? - Group 2 is blue, but doesn’t contains any blue layers…
Do you have to export layer Group 2 projection or not? - Group 3 is blue, but doesn’t contains blue layers, but contains layer with blue sub-layers
Grum999
My plugin is close to done. To be clear, the layers I want to filter out are the blue-tagged layers (because CSP’s draft layer system used to represent draft layers with a non-photo blue pencil symbol), and like in CSP my goal is for any group that is tagged blue to have all its contents also filtered out.
Here’s my current recursion:
def recursion(someCurrNode, tempNode):
if someCurrNode.visible() == True and someCurrNode.colorLabel() != 1:
if someCurrNode.type() == "grouplayer":
groupCopy = someCurrNode.duplicate()
for i in groupCopy.childNodes():
if i.colorLabel() == 1:
groupCopy.removeChildNode(i)
if i.type() == "grouplayer":
groupCopy.removeChildNode(i)
recursion(i, groupCopy)
tempNode.addChildNode(groupCopy, None)
else:
layerCopy = someCurrNode.duplicate()
tempNode.addChildNode(layerCopy, None)
The first tempNode is the root of the new temporary document. The strategy for the groups is to copy a whole group and then subtract from it before adding it to the temp stack; I think that’s the simplest way to carry over a group with all the properties retained, but I would like to know if there’s a better solution.
One small side effect I found is that transparency masks (added to a group) are carried over to the temp stack in unexpected ways:
If the transparency mask is visible in the original document, it is applied to the temp document’s visible layers.
If the transparency mask is toggled off in the original document, it is added to the temp document’s stack, but it’s still toggled off.
These don’t matter for the PNG export, but I’m wondering if that’s a sign for issues that I’ll have to check for other layer types.
I’ll test this more and see what I want to do about non-default export options (probably just get the regular export menu back), and share it when it’s ready.
Before export, do a refresh projection.
it should solve the problem I think
Grum999
Thanks again. I’ll be trying that next time I can work on this… as well as figuring out a bug I just found when using it with another script I made. I don’t know which script is at fault here, but my blue filter works on any blue layers added manually but not on the blue layer within the group added using this script (Note that colorLabel 1 is blue.):
## Script to create a group consisting of:
## Colors, Values, and Sketch
## The Colors layer is set to the 'Color' blending mode
## and its alpha inheritance is on.
from krita import *
doc = Krita.instance().activeDocument()
if not doc is None:
root = doc.rootNode();
group = doc.createGroupLayer("Colors-Values-Sketch Group")
root.addChildNode(group, None)
layerNames = ["Sketch", "Values", "Colors"]
layerColors = [1, 8, 7]
# Generate the bottom two layers.
for i in range(len(layerNames)-1):
newLayer = doc.createNode(layerNames[i], "paintLayer")
newLayer.setColorLabel(layerColors[i])
group.addChildNode(newLayer, None)
# The Colors layer has a couple more settings to set.
colorsLayer = doc.createNode(layerNames[2], "paintLayer")
colorsLayer.setColorLabel(layerColors[2])
colorsLayer.setBlendingMode("color")
colorsLayer.setInheritAlpha(True)
group.addChildNode(colorsLayer, None)
Not sure to understand…
On my side, the problem is on first script and then problem should occurs id blue tag is assigned manually or programmatically
In recursion()
method, change:
if i.colorLabel() == 1:
groupCopy.removeChildNode(i)
if i.type() == "grouplayer":
groupCopy.removeChildNode(i)
recursion(i, groupCopy)
With:
if i.type() == "grouplayer":
groupCopy.removeChildNode(i)
recursion(i, groupCopy)
elif i.colorLabel() != 1:
groupCopy.removeChildNode(i)
Grum999
That version messes up the case of: [Non-Blue Group]->[Blue Layer], though just replacing if with elif in my own version makes sense and works; it looks like it even fixed the bug that I described earlier, somehow.
That script I posted just generates a group with three layers (one of which is a blue layer) and somehow the script of this topic wasn’t filtering the blue layer from that specific group.
Lastly, I should figure out the thing about transparency masks.
Here’s the current code snippet for this part:
# Make a new Krita document of the same dimensions.
# A Krita document is made with a Background layer that
# should be turned off.
tempDoc = Krita.instance().createDocument(currDoc.width(),\
currDoc.height(), "tempDoc", "RGBA", "U8", "", 300.0)
tempDoc.setBatchmode(True)
tempRoot = tempDoc.rootNode()
tempDoc.nodeByName("Background").setVisible(False)
# Copy over the layers that are visible and not tagged blue.
# colorLabel 1 is blue.
currRoot = currDoc.rootNode()
for i in currRoot.childNodes():
recursion(i, tempRoot)
tempDoc.refreshProjection()
tempDoc.saveAs(fileName)
# Disable tempDoc.close() and enable this
# to check the layer stack.
Krita.instance().activeWindow().addView(tempDoc)
#tempDoc.close()
Here’s the original stack:
and here’s the temp stack:
Transparency Mask 1 moves from above 2b to below 2b. Like you said, adding the refreshProjection() did fix the issue where it sometimes doesn’t appear. It looks like that doesn’t make a difference, though I’m still wondering how that could happen with the copying function if it’s just a node like any other.