| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- from krita import DockWidget
- from PyQt5.QtWidgets import QWidget, QDialog, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QComboBox, QCheckBox, QPushButton
- DOCKER_TITLE = 'Bleed Generator'
- class BleedGenerator(DockWidget):
- def __init__(self):
- super().__init__()
- self.setWindowTitle(DOCKER_TITLE)
- widget = QWidget()
- layout = QVBoxLayout()
- widget.setLayout(layout)
- layout.addSpacing(10)
- layout.addWidget(QLabel('Adds bleed to the active paint layer.'))
- # Count and unit inputs. Default to 24px (2mm at 300dpi).
- self.countInput = QLineEdit('24')
- self.unitInput = QComboBox()
- self.unitInput.addItems( ['px', 'mm', 'inch'] )
- row1 = QHBoxLayout()
- row1.addWidget(QLabel('Bleed amount:'))
- row1.addWidget(self.countInput)
- row1.addWidget(self.unitInput)
- layout.addLayout(row1)
- # Option to bleed to boundaries.
- self.bleedToBoundsCheck = QCheckBox('Bleed to nearest guides/boundaries')
- self.bleedToBoundsCheck.setChecked(False)
- layout.addWidget(self.bleedToBoundsCheck)
- # Offset input i.e. how far from edge to start copying lines.
- self.offsetInput = QLineEdit('0')
- row2 = QHBoxLayout()
- row2.addWidget(QLabel('Offset (0 = use edge):'))
- row2.addWidget(self.offsetInput)
- row2.addWidget(QLabel('px'))
- layout.addLayout(row2)
- # Side selection
- self.topCheck = QCheckBox('Top')
- self.topCheck.setChecked(True)
- self.botCheck = QCheckBox('Bottom')
- self.botCheck.setChecked(True)
- self.leftCheck = QCheckBox('Left')
- self.leftCheck.setChecked(True)
- self.rightCheck = QCheckBox('Right')
- self.rightCheck.setChecked(True)
- layout.addWidget(QLabel('Sides to bleed:'))
- row3 = QHBoxLayout()
- row3.addWidget(self.topCheck)
- row3.addWidget(self.botCheck)
- row3.addWidget(self.leftCheck)
- row3.addWidget(self.rightCheck)
- layout.addLayout(row3)
- # Button!
- layout.addSpacing(20)
- goButton = QPushButton("Add Bleed")
- 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.generateBleedLoop )
- # notifies when views are added or removed
- # 'pass' means do not do anything
- def canvasChanged(self, canvas):
- pass
- ##########
- # Slots
- ##########
- def generateBleedLoop(self, e):
- self.doc = Krita.instance().activeDocument()
- # Get the selected layer(s).
- w = Krita.instance().activeWindow()
- v = w.activeView()
- layers = v.selectedNodes()
- didNotRun = True
- for layer in layers:
- if layer.type() == 'paintlayer':
- self.generateBleed(layer)
- didNotRun = False
- if didNotRun:
- dialog = QDialog()
- dialog.setWindowTitle("Paint Layer Required")
- layout = QVBoxLayout()
- layout.addWidget(QLabel('Bleed Generator only works on paint layers. Please select one.'))
- dialog.setLayout(layout)
- dialog.exec_()
- else:
- # Refresh the view, or changes will not be immediately reflected.
- self.doc.refreshProjection()
- # Actually generates the bleed.
- def generateBleed(self, layer):
- self.offset = round(float(self.offsetInput.text()))
- # Calculate how many lines of pixels to copy for each side
- amounts = self.calculateBleedAmounts(layer)
- # Copy lines for selected sides.
- # Bounds are fetched each time as layer dimensions change.
- if self.topCheck.checkState():
- bds = layer.bounds()
- xpos = bds.left()
- ypos = bds.top() - self.offset
- len = bds.width()
- bleedline = layer.pixelData(xpos, ypos, len, 1)
- for c in range(1, amounts['top']+1):
- layer.setPixelData(bleedline, xpos, (ypos - c), len, 1)
- if self.botCheck.checkState():
- bds = layer.bounds()
- xpos = bds.left()
- ypos = bds.bottom() + self.offset
- len = bds.width()
- bleedline = layer.pixelData(xpos, ypos, len, 1)
- for c in range(1, amounts['bottom']+1):
- layer.setPixelData(bleedline, xpos, (ypos + c), len, 1)
- if self.leftCheck.checkState():
- bds = layer.bounds()
- xpos = bds.left() - self.offset
- ypos = bds.top()
- len = bds.height()
- bleedline = layer.pixelData(xpos, ypos, 1, len)
- for c in range(1, amounts['left']+1):
- layer.setPixelData(bleedline, (xpos - c), ypos, 1, len)
- if self.rightCheck.checkState():
- bds = layer.bounds()
- xpos = bds.right() + self.offset
- ypos = bds.top()
- len = bds.height()
- bleedline = layer.pixelData(xpos, ypos, 1, len)
- for c in range(1, amounts['right']+1):
- layer.setPixelData(bleedline, (xpos + c), ypos, 1, len)
- # Refresh the view, or the cloned pixels will not be immediately shown.
- self.doc.refreshProjection()
- def calculateBleedAmounts(self, layer):
- amounts = {}
- if self.bleedToBoundsCheck.checkState():
- # Bleed amounts determined from boundaries.
- # Get all boundaries.
- vPoints = self.doc.horizontalGuides()
- hPoints = self.doc.verticalGuides()
- vPoints.insert(0, 0)
- vPoints.append(self.doc.height())
- vPoints.sort()
- hPoints.insert(0, 0)
- hPoints.append(self.doc.width())
- hPoints.sort()
- bds = layer.bounds()
- # Calculate bleed from layer's edge to next relevant boundary.
- prevV = 0
- top = bds.top()
- bottom = bds.bottom() + 1
- for v in vPoints:
- v = round(v)
- if 'top' not in amounts and v >= top:
- amounts['top'] = top - prevV
- if 'bottom' not in amounts and v > bottom:
- amounts['bottom'] = v - bottom
- break
- prevV = v
- prevH = 0
- left = bds.left()
- right = bds.right() + 1
- for h in hPoints:
- h = round(h)
- if 'left' not in amounts and h >= left:
- amounts['left'] = left - prevH
- if 'right' not in amounts and h > right:
- amounts['right'] = h - right
- prevH = h
- else:
- # Bleed amounts based on the user's input.
- unit = self.unitInput.currentIndex()
- count = float(self.countInput.text())
- ppi = self.doc.resolution()
- if unit == 1: # mm
- count = count * (ppi / 24.5)
- if unit == 2: # inch
- count = count * ppi
- count = round(count) - self.offset
- amounts['top'] = count
- amounts['bottom'] = count
- amounts['left'] = count
- amounts['right'] = count
- return amounts
|