Explorar o código

Add Page Slicer

This will be a plugin to cut an image up via page guides.
cinaeco %!s(int64=4) %!d(string=hai) anos
pai
achega
bcc42493f9

+ 8 - 0
page_slicer_docker.desktop

@@ -0,0 +1,8 @@
+[Desktop Entry]
+Type=Service
+ServiceTypes=Krita/PythonPlugin
+X-KDE-Library=page_slicer_docker
+X-Python-2-Compatible=false
+X-Krita-Manual=Manual.html
+Name=Page Slicer Docker
+Comment=Divides an image up according to horizontal and vertical guides and creates a new layer for each detected section.

+ 32 - 0
page_slicer_docker/Manual.html

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head><title>Page Slicer Documentation</title>
+</head>
+<body>
+<h3>Page Slicer Documentation</h3>
+
+<p>
+This extension allows a user to generate new layers with segments of an image,
+outlined by rectangles formed using horizontal and vertical guides.
+</p>
+
+<p>
+Users can choose whether to include the image borders in the calculations. The
+default behaviour is to only extract image segments enclosed within guides. At
+least 4 guides (2 vertical, 2 horizontal) are needed in this case.
+</p>
+
+<p>
+Users can also choose to ignore sections below or above certain thresholds e.g.
+sections that are less than 100 pixels wide or greater than 1000 pixels tall.
+</p>
+
+<h3>Usage</h3>
+
+Select a paint layer. Set up at least 4 guides to enclose a section of an image
+(or just 1 guide, if image borders are used). Press the "Slice" button.
+
+</body>
+</html>

+ 10 - 0
page_slicer_docker/__init__.py

@@ -0,0 +1,10 @@
+from krita import DockWidgetFactory, DockWidgetFactoryBase
+from .page_slicer_docker import PageSlicer
+
+DOCKER_ID = 'page_slicer'
+instance = Krita.instance()
+dock_widget_factory = DockWidgetFactory(DOCKER_ID,
+                                        DockWidgetFactoryBase.DockRight,
+                                        PageSlicer)
+
+instance.addDockWidgetFactory(dock_widget_factory)

+ 146 - 0
page_slicer_docker/page_slicer_docker.py

@@ -0,0 +1,146 @@
+from krita import DockWidget
+from PyQt5.QtWidgets import QWidget, QDialog, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QComboBox, QCheckBox, QPushButton
+
+DOCKER_TITLE = 'Page Slicer'
+
+class PageSlicer(DockWidget):
+
+    def __init__(self):
+        super().__init__()
+        self.setWindowTitle(DOCKER_TITLE)
+        widget = QWidget()
+        layout = QVBoxLayout()
+        widget.setLayout(layout)
+
+        layout.addSpacing(10)
+        layout.addWidget(QLabel('Slice up an image into segments using page guides.'))
+
+        # Thresholds for ignoring segments. Defaults to 100px in both dimensions.
+        # limitModes = ['<', '>']
+        self.widthInput = QLineEdit('100')
+        # self.widthLimitMode = QComboBox()
+        # self.widthLimitMode.addItems(limitModes)
+        self.heightInput = QLineEdit('100')
+        # self.heightLimitMode = QComboBox()
+        # self.heightLimitMode.addItems(limitModes)
+        layout.addWidget(QLabel('Segment-Ignore Thresholds:'))
+        row1 = QHBoxLayout()
+        row1.addWidget(QLabel('Width <'))
+        # row1.addWidget(self.widthLimitMode)
+        row1.addWidget(self.widthInput)
+        row1.addWidget(QLabel('px'))
+        layout.addLayout(row1)
+        row2 = QHBoxLayout()
+        row2.addWidget(QLabel('Height <'))
+        # row2.addWidget(self.heightLimitMode)
+        row2.addWidget(self.heightInput)
+        row2.addWidget(QLabel('px'))
+        layout.addLayout(row2)
+
+        # Use image bounds or not
+        self.useImageBoundsCheck = QCheckBox('Use Image Bounds')
+        self.useImageBoundsCheck.setChecked(False)
+        layout.addWidget(self.useImageBoundsCheck)
+
+        # Button!
+        layout.addSpacing(20)
+        goButton = QPushButton("Slice")
+        goButton.setIcon( Krita.instance().icon('animation_play') )
+        layout.addWidget(goButton)
+
+        # Add a stretch to prevent the rest of the content from stretching.
+        layout.addStretch()
+
+        # Add widget to the docker.
+        self.setWidget(widget)
+
+        # Hook up the action to the button.
+        goButton.clicked.connect( self.slicePage )
+
+
+    # notifies when views are added or removed
+    # 'pass' means do not do anything
+    def canvasChanged(self, canvas):
+        pass
+
+
+    ##########
+    # Slots
+    ##########
+
+    # Actually slices the page.
+    def slicePage(self, e):
+
+        # Get the current layer.
+        doc = Krita.instance().activeDocument()
+        root = doc.rootNode()
+        layer = doc.activeNode()
+        if layer.type() != 'paintlayer':
+            dialog = QDialog()
+            dialog.setWindowTitle("Paint Layer Required")
+            layout = QVBoxLayout()
+            layout.addWidget(QLabel('Page slicer only works on paint layers. Please select one.'))
+            dialog.setLayout(layout)
+            dialog.exec_()
+            return
+
+        # Get horizontal and vertical points.
+        # Horizontal guides provide the Vertical y-axis points and vice versa.
+        vPoints = doc.horizontalGuides()
+        hPoints = doc.verticalGuides()
+
+        # Add the edges of the image for segment calculation, if needed.
+        useImageBounds = self.useImageBoundsCheck.checkState()
+        if useImageBounds:
+            vPoints.insert(0, 0)
+            vPoints.append(doc.height())
+            hPoints.insert(0, 0)
+            hPoints.append(doc.width())
+
+        # Sanitize points. Guide values are floats, and may contain unexpected
+        # extra fractional values.
+        vPoints = [round(v) for v in vPoints]
+        vPoints.sort()
+        hPoints = [round(h) for h in hPoints]
+        hPoints.sort()
+
+        # Check that there are enough vertical and horizontal points.
+        # When using image bounds there must be at least 1 guide (V or H).
+        # When not using image bounds, there must be at least 4 guides (2V, 2H).
+        imageBoundsLimitFail = True if len(vPoints) + len(hPoints) < 5 else False
+        nonImageBoundsLimitFail = True if len(vPoints) < 2 or len(hPoints) < 2 else False
+        if (useImageBounds and imageBoundsLimitFail) or nonImageBoundsLimitFail:
+            dialog = QDialog()
+            dialog.setWindowTitle("Insufficient Guides!")
+            layout = QVBoxLayout()
+            if useImageBounds:
+                layout.addWidget(QLabel('Have at least 1 horizontal or vertical guide.'))
+            else:
+                layout.addWidget(QLabel('Have at least 2 horizontal and 2 vertical guides.'))
+            dialog.setLayout(layout)
+            dialog.exec_()
+            return
+
+        # Find segments, row by row.
+        startV = vPoints.pop(0)
+        startH = hPoints.pop(0)
+        prevV = startV
+        count = 1
+        heightLimit = int(self.heightInput.text())
+        widthLimit = int(self.widthInput.text())
+        for v in vPoints:
+            segHeight = v - prevV
+            if segHeight >= heightLimit:
+                prevH = startH
+                for h in hPoints:
+                    segWidth = h - prevH
+                    if segWidth >= widthLimit:
+                        # Copy image data to new layer.
+                        segmentData = layer.pixelData(prevH, prevV, segWidth, segHeight)
+                        newLayer = doc.createNode('seg' + str(count), 'paintLayer')
+                        newLayer.setPixelData(segmentData, prevH, prevV, segWidth, segHeight)
+                        root.addChildNode(newLayer, None)
+                        count += 1
+                    prevH = h
+            prevV = v
+