diff --git a/KCC-Linux.ui b/KCC-Linux.ui index 8034019..6d8eaba 100644 --- a/KCC-Linux.ui +++ b/KCC-Linux.ui @@ -416,7 +416,7 @@ p, li { white-space: pre-wrap; } Qt::NoFocus - <html><head/><body><p>Disable page spliting.<br/>They will be rotated instead.</p></body></html> + <html><head/><body><p>Disable splitting of two-page spreads.<br/>They will be rotated instead.</p></body></html> Horizontal mode @@ -543,9 +543,6 @@ p, li { white-space: pre-wrap; } DejaVu Sans - - <html><head/><body><p>When converting color images setting this option to 1.0 <span style=" font-weight:600;">might</span> improve readability.</p></body></html> - Gamma: Auto @@ -567,9 +564,6 @@ p, li { white-space: pre-wrap; } Qt::ClickFocus - - <html><head/><body><p>When converting color images setting this option to 1.0 <span style=" font-weight:600;">might</span> improve readability.</p></body></html> - 500 @@ -641,7 +635,7 @@ p, li { white-space: pre-wrap; } Qt::NoFocus - Do not convert images to grayscale. + <html><head/><body><p>Don't convert images to grayscale.</p></body></html> Color mode diff --git a/KCC-OSX.ui b/KCC-OSX.ui index 1c7c5ae..4896197 100644 --- a/KCC-OSX.ui +++ b/KCC-OSX.ui @@ -416,7 +416,7 @@ Qt::NoFocus - <html><head/><body><p><span style=" font-size:12pt;">Disable page spliting.<br/>They will be rotated instead.</span></p></body></html> + <html><head/><body><p><span style=" font-size:12pt;">Disable splitting of two-page spreads.<br/>They will be rotated instead.</span></p></body></html> Horizontal mode @@ -543,9 +543,6 @@ false - - <html><head/><body><p><span style=" font-size:12pt;">When converting color images setting this option to 1.0 </span><span style=" font-size:12pt; font-weight:600;">might</span><span style=" font-size:12pt;"> improve readability.</span></p></body></html> - Gamma: Auto @@ -567,9 +564,6 @@ Qt::ClickFocus - - <html><head/><body><p><span style=" font-size:12pt;">When converting color images setting this option to 1.0 </span><span style=" font-size:12pt; font-weight:600;">might</span><span style=" font-size:12pt;"> improve readability.</span></p></body></html> - 500 @@ -645,7 +639,7 @@ Qt::NoFocus - <html><head/><body><p><span style=" font-size:12pt;">Do not convert images to grayscale.</span></p></body></html> + <html><head/><body><p><span style=" font-size:12pt;">Don't convert images to grayscale.</span></p></body></html> Color mode diff --git a/KCC.ui b/KCC.ui index edb433a..6725915 100644 --- a/KCC.ui +++ b/KCC.ui @@ -363,7 +363,7 @@ p, li { white-space: pre-wrap; } Qt::NoFocus - <html><head/><body><p>Disable page spliting.<br/>They will be rotated instead.</p></body></html> + <html><head/><body><p>Disable splitting of two-page spreads.<br/>They will be rotated instead.</p></body></html> Horizontal mode @@ -469,9 +469,6 @@ p, li { white-space: pre-wrap; } 40 - - When converting color images setting this option to 1.0 MIGHT improve readability. - Gamma: Auto @@ -488,9 +485,6 @@ p, li { white-space: pre-wrap; } Qt::ClickFocus - - <html><head/><body><p>When converting color images setting this option to 1.0 <span style=" font-weight:600;">might</span> improve readability.</p></body></html> - 500 @@ -555,7 +549,7 @@ p, li { white-space: pre-wrap; } Qt::NoFocus - Do not convert images to grayscale. + <html><head/><body><p>Don't convert images to grayscale.</p></body></html> Color mode diff --git a/README.md b/README.md index 2534614..fcf64e5 100644 --- a/README.md +++ b/README.md @@ -10,22 +10,21 @@ It can also optionally optimize images by applying a number of transformations. Amazon's tool is for comic publishers and involves a lot of manual effort, while **KCC** is for comic readers. _KC2_ in no way is a replacement for **KCC** so you can be quite confident we'll going to carry on developing our little monster ;-) -### Donations -If you find **KCC** valuable you can consider donating to the authors: +### Issues / new features / donations +If you have some problems using KCC please [file an issue here](https://github.com/ciromattia/kcc/issues/new). +If you can fix an open issue, fork & make a pull request. +If you want more chances an issue is fixes or your wanted feature added, consider [placing a bounty](https://www.bountysource.com/trackers/65571-ciromattia-kcc)! -* Ciro Mattia Gonano - * PayPal: [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=D8WNYNPBGDAS2) - * Flattr: [![Flattr this](http://api.flattr.com/button/flattr-badge-large.png)](http://flattr.com/thing/2260449/ciromattiakcc-on-GitHub) -* Paweł Jastrzębski - * PayPal: [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YTTJ4LK2JDHPS) - * BitCoin: [1W15wwqsfd7wbaZ6wvSJf1LW1bz6q5L8b](bitcoin:1W15wwqsfd7wbaZ6wvSJf1LW1bz6q5L8b?label=KCC) +If you find **KCC** valuable you can consider donating to the authors: + * Ciro Mattia Gonano: [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=D8WNYNPBGDAS2) [![Flattr this](http://api.flattr.com/button/flattr-badge-large.png)](http://flattr.com/thing/2260449/ciromattiakcc-on-GitHub) + * Paweł Jastrzębski: [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YTTJ4LK2JDHPS) [![1W15wwqsfd7wbaZ6wvSJf1LW1bz6q5L8b](http://s30.postimg.org/6z3kwvdlp/BC_Rnd.png)](bitcoin:1W15wwqsfd7wbaZ6wvSJf1LW1bz6q5L8b?label=KCC) [1W15wwqsfd7wbaZ6wvSJf1LW1bz6q5L8b](bitcoin:1W15wwqsfd7wbaZ6wvSJf1LW1bz6q5L8b?label=KCC) ## BINARY RELEASES You can find the latest released binary at the following links: - **Windows:** [http://kcc.vulturis.eu/Windows/](http://kcc.vulturis.eu/Windows/) - **Linux:** [http://kcc.vulturis.eu/Linux/](http://kcc.vulturis.eu/Linux/) - **OS X (10.8 or later):** [http://kcc.vulturis.eu/OSX/](http://kcc.vulturis.eu/OSX/) -- **OS X (10.7 or earlier):** [http://kcc.vulturis.eu/Old/OSX/KindleComicConverter_3.6-rc1_osx10.7.zip](http://kcc.vulturis.eu/Old/OSX/KindleComicConverter_3.6-rc1_osx10.7.zip) +- **OS X (10.7 or earlier):** Soon™ ## INPUT FORMATS **KCC** can understand and convert, at the moment, the following file types: @@ -55,10 +54,10 @@ You can find the latest released binary at the following links: * Read tooltip of _High/Ultra quality_ option. There are many important informations there. * When converting images smaller than device resolution remember to enable upscaling. * Panel View (auto zooming every part of page) can be disabled directly on Kindle. There is no KCC option to do that. -* If you're converting color images and the end result is not satisfactory, experiment with gamma correction option (check 1.0 setting first). * Check our [wiki](https://github.com/ciromattia/kcc/wiki/Other-devices) for a list of tested Non-Kindle E-Readers. * The first image found will be set as the comic's cover. * All files/directories will be added to EPUB in alphabetical order. +* Using high/ultra quality output option with Kindle Fire HD/HDX in most cases is just waste of space. * ComicRack metadata will be parsed only if they are saved in *ComicInfo.xml* file. ### Calibre: @@ -295,6 +294,22 @@ The app relies and includes the following scripts/binaries: * GUI tweaks and minor bug fixes ####3.6: +* Increased quality of Panel View zoom +* Creation of multipart MOBI output is now faster on machines with 4GB+ RAM +* Automatic gamma correction now distinguishes color and grayscale images +* Added ComicRack metadata parser +* Implemented new method to detect border color in non-webtoon comics +* Upscaling is now enabled by default for Kindle Fire HD/HDX +* Windows nad Linux releases now have tray icon +* Fixed Kindle Fire HDX 7" output +* Increased target resolution for Kindle DX/DXG CBZ output + +####3.6.1: +* Fixed PNG output + +####3.6.2: +* Fixed previous PNG output fix +* Fixed Panel View anomalies ## COPYRIGHT diff --git a/kcc/KCC_gui.py b/kcc/KCC_gui.py index 0142767..5d0dda4 100644 --- a/kcc/KCC_gui.py +++ b/kcc/KCC_gui.py @@ -291,7 +291,7 @@ class KindleUnpackThread(QtCore.QRunnable): try: # MOBI file produced by KindleGen is hybrid. KF8 + M7 + Source header # KindleSplit is removing redundant data as we need only KF8 part for new Kindle models - if profile in ['K345', 'KHD', 'KF', 'KFHD', 'KFHD8', 'KFHDX8', 'KFA']: + if profile in ['K345', 'KHD', 'KF', 'KFHD', 'KFHD8', 'KFHDX', 'KFHDX8', 'KFA']: newKindle = True else: newKindle = False @@ -356,6 +356,9 @@ class WorkerThread(QtCore.QThread): argv.append("--quality=1") elif GUI.QualityBox.checkState() == 2: argv.append("--quality=2") + if GUI.currentMode == 1: + if profile in ['KFHD', 'KFHD8', 'KFHDX', 'KFHDX8']: + argv.append("--upscale") if GUI.currentMode > 1: if GUI.ProcessingBox.isChecked(): argv.append("--noprocessing") @@ -374,6 +377,7 @@ class WorkerThread(QtCore.QThread): if GUI.WebtoonBox.isChecked(): argv.append("--webtoon") if float(GUI.GammaValue) > 0.09: + # noinspection PyTypeChecker argv.append("--gamma=" + GUI.GammaValue) if str(GUI.FormatBox.currentText()) == 'CBZ': argv.append("--cbz-output") @@ -540,11 +544,11 @@ class WorkerThread(QtCore.QThread): class SystemTrayIcon(QtGui.QSystemTrayIcon): def __init__(self, parent=None): if not sys.platform.startswith('darwin') and self.isSystemTrayAvailable(): - QtGui.QSystemTrayIcon.__init__(self, parent) - self.setIcon(GUI.icons.programIcon) + QtGui.QSystemTrayIcon.__init__(self, GUI.icons.programIcon, MW) self.activated.connect(self.catchClicks) def catchClicks(self): + MW.showNormal() MW.raise_() MW.activateWindow() @@ -793,9 +797,12 @@ class KCCGUI(KCC_ui.Ui_KCC): GUI.QualityBox.setChecked(False) GUI.QualityBox.setEnabled(False) self.QualityBoxDisabled = True + if value in [4, 5, 6, 7]: + if GUI.UpscaleBox.isEnabled(): + GUI.UpscaleBox.setChecked(True) else: if not GUI.WebtoonBox.isChecked() and not GUI.ProcessingBox.isChecked() \ - and str(GUI.FormatBox.currentText()) != 'CBZ': + and str(GUI.FormatBox.currentText()) != 'CBZ' and value not in [9, 11, 12, 13]: GUI.QualityBox.setEnabled(True) self.QualityBoxDisabled = False @@ -880,8 +887,6 @@ class KCCGUI(KCC_ui.Ui_KCC): event.ignore() if not GUI.ConvertButton.isEnabled(): event.ignore() - if not sys.platform.startswith('darwin'): - self.tray.hide() self.contentServer.stop() self.settings.setValue('settingsVersion', __version__) self.settings.setValue('lastPath', self.lastPath) @@ -978,9 +983,9 @@ class KCCGUI(KCC_ui.Ui_KCC): self.tray.show() statusBarLabel = QtGui.QLabel('HOMEPAGE - DONATE - README - WIKI') + 'ciromattia/kcc/blob/master/README.md#issues--new-features--donations">DONATE' + ' - README<' + '/a> - WIKI') statusBarLabel.setAlignment(QtCore.Qt.AlignCenter) statusBarLabel.setStyleSheet(self.statusBarStyle) statusBarLabel.setOpenExternalLinks(True) diff --git a/kcc/KCC_ui.py b/kcc/KCC_ui.py index 65c71a6..b530932 100644 --- a/kcc/KCC_ui.py +++ b/kcc/KCC_ui.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'KCC.ui' # -# Created: Tue Nov 12 14:31:48 2013 +# Created: Fri Dec 13 19:22:05 2013 # by: PyQt4 UI code generator 4.10.3 # # WARNING! All changes made in this file will be lost! @@ -301,14 +301,12 @@ class Ui_KCC(object): "

Indeterminate - High quality mode
Not zoomed image might be a little blurry.
- Medium/High quality when zoom is not enabled.
- Maximum quality when zoom is enabled.

\n" "

Checked - Ultra quality mode
Maximum possible quality.
- Maximum quality when zoom is not enabled.
- Maximum quality when zoom is enabled.
- Very high file size.

", None)) self.QualityBox.setText(_translate("KCC", "High/Ultra quality", None)) - self.RotateBox.setToolTip(_translate("KCC", "

Disable page spliting.
They will be rotated instead.

", None)) + self.RotateBox.setToolTip(_translate("KCC", "

Disable splitting of two-page spreads.
They will be rotated instead.

", None)) self.RotateBox.setText(_translate("KCC", "Horizontal mode", None)) self.BasicModeButton.setText(_translate("KCC", "Basic", None)) self.AdvModeButton.setText(_translate("KCC", "Advanced", None)) - self.GammaLabel.setToolTip(_translate("KCC", "When converting color images setting this option to 1.0 MIGHT improve readability.", None)) self.GammaLabel.setText(_translate("KCC", "Gamma: Auto", None)) - self.GammaSlider.setToolTip(_translate("KCC", "

When converting color images setting this option to 1.0 might improve readability.

", None)) - self.ColorBox.setToolTip(_translate("KCC", "Do not convert images to grayscale.", None)) + self.ColorBox.setToolTip(_translate("KCC", "

Don\'t convert images to grayscale.

", None)) self.ColorBox.setText(_translate("KCC", "Color mode", None)) self.wLabel.setToolTip(_translate("KCC", "Resolution of target device.", None)) self.wLabel.setText(_translate("KCC", "Custom width: ", None)) diff --git a/kcc/KCC_ui_linux.py b/kcc/KCC_ui_linux.py index c99227d..711556b 100644 --- a/kcc/KCC_ui_linux.py +++ b/kcc/KCC_ui_linux.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'KCC-Linux.ui' # -# Created: Tue Nov 12 14:32:11 2013 +# Created: Fri Dec 13 19:22:17 2013 # by: PyQt4 UI code generator 4.10.3 # # WARNING! All changes made in this file will be lost! @@ -370,14 +370,12 @@ class Ui_KCC(object): "

Indeterminate - High quality mode
Not zoomed image might be a little blurry.
- Medium/High quality when zoom is not enabled.
- Maximum quality when zoom is enabled.

\n" "

Checked - Ultra quality mode
Maximum possible quality.
- Maximum quality when zoom is not enabled.
- Maximum quality when zoom is enabled.
- Very high file size.

", None)) self.QualityBox.setText(_translate("KCC", "High/Ultra quality", None)) - self.RotateBox.setToolTip(_translate("KCC", "

Disable page spliting.
They will be rotated instead.

", None)) + self.RotateBox.setToolTip(_translate("KCC", "

Disable splitting of two-page spreads.
They will be rotated instead.

", None)) self.RotateBox.setText(_translate("KCC", "Horizontal mode", None)) self.BasicModeButton.setText(_translate("KCC", "Basic", None)) self.AdvModeButton.setText(_translate("KCC", "Advanced", None)) - self.GammaLabel.setToolTip(_translate("KCC", "

When converting color images setting this option to 1.0 might improve readability.

", None)) self.GammaLabel.setText(_translate("KCC", "Gamma: Auto", None)) - self.GammaSlider.setToolTip(_translate("KCC", "

When converting color images setting this option to 1.0 might improve readability.

", None)) - self.ColorBox.setToolTip(_translate("KCC", "Do not convert images to grayscale.", None)) + self.ColorBox.setToolTip(_translate("KCC", "

Don\'t convert images to grayscale.

", None)) self.ColorBox.setText(_translate("KCC", "Color mode", None)) self.wLabel.setToolTip(_translate("KCC", "Resolution of target device.", None)) self.wLabel.setText(_translate("KCC", "Custom width: ", None)) diff --git a/kcc/KCC_ui_osx.py b/kcc/KCC_ui_osx.py index 175a3c4..23b3321 100644 --- a/kcc/KCC_ui_osx.py +++ b/kcc/KCC_ui_osx.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'KCC-OSX.ui' # -# Created: Tue Nov 12 14:31:59 2013 +# Created: Fri Dec 13 19:22:27 2013 # by: PyQt4 UI code generator 4.10.3 # # WARNING! All changes made in this file will be lost! @@ -388,14 +388,12 @@ class Ui_KCC(object): self.MangaBox.setText(_translate("KCC", "Manga mode", None)) self.QualityBox.setToolTip(_translate("KCC", "

Unchecked - Normal quality mode
Use it when Panel View support is not needed.
- Maximum quality when zoom is not enabled.
- Poor quality when zoom is enabled.
- Lowest file size.

Indeterminate - High quality mode
Not zoomed image might be a little blurry.
- Medium/High quality when zoom is not enabled.
- Maximum quality when zoom is enabled.

Checked - Ultra quality mode
Maximum possible quality.
- Maximum quality when zoom is not enabled.
- Maximum quality when zoom is enabled.
- Very high file size.

", None)) self.QualityBox.setText(_translate("KCC", "High/Ultra quality", None)) - self.RotateBox.setToolTip(_translate("KCC", "

Disable page spliting.
They will be rotated instead.

", None)) + self.RotateBox.setToolTip(_translate("KCC", "

Disable splitting of two-page spreads.
They will be rotated instead.

", None)) self.RotateBox.setText(_translate("KCC", "Horizontal mode", None)) self.BasicModeButton.setText(_translate("KCC", "Basic", None)) self.AdvModeButton.setText(_translate("KCC", "Advanced", None)) - self.GammaLabel.setToolTip(_translate("KCC", "

When converting color images setting this option to 1.0 might improve readability.

", None)) self.GammaLabel.setText(_translate("KCC", "Gamma: Auto", None)) - self.GammaSlider.setToolTip(_translate("KCC", "

When converting color images setting this option to 1.0 might improve readability.

", None)) - self.ColorBox.setToolTip(_translate("KCC", "

Do not convert images to grayscale.

", None)) + self.ColorBox.setToolTip(_translate("KCC", "

Don\'t convert images to grayscale.

", None)) self.ColorBox.setText(_translate("KCC", "Color mode", None)) self.wLabel.setToolTip(_translate("KCC", "

Resolution of target device.

", None)) self.wLabel.setText(_translate("KCC", "Custom width: ", None)) diff --git a/kcc/cbxarchive.py b/kcc/cbxarchive.py index ae70a0a..17a612c 100644 --- a/kcc/cbxarchive.py +++ b/kcc/cbxarchive.py @@ -23,8 +23,20 @@ import os import zipfile from . import rarfile import locale +from sys import platform from subprocess import STDOUT, PIPE -from psutil import Popen +try: + #noinspection PyUnresolvedReferences + from psutil import Popen +except ImportError: + print("ERROR: Psutil is not installed!") + if platform.startswith('linux'): + import Tkinter + import tkMessageBox + importRoot = Tkinter.Tk() + importRoot.withdraw() + tkMessageBox.showerror("KCC - Error", "Psutil is not installed!") + exit(1) from shutil import move diff --git a/kcc/comic2ebook.py b/kcc/comic2ebook.py index de215c1..364e1f9 100755 --- a/kcc/comic2ebook.py +++ b/kcc/comic2ebook.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python2 # -*- coding: utf-8 -*- # # Copyright (c) 2012-2013 Ciro Mattia Gonano @@ -106,9 +106,9 @@ def buildHTML(path, imgfile): elif noHorizontalPV and not noVerticalPV: if rotatedPage: if options.righttoleft: - order = [2, 1] - else: order = [1, 2] + else: + order = [2, 1] else: order = [1, 2] boxes = ["BoxT", "BoxB"] @@ -130,35 +130,48 @@ def buildHTML(path, imgfile): "}'>\n"]) if options.quality == 2: imgfilepv = string.split(imgfile, ".") - imgfilepv[0] = imgfilepv[0].split("_kccx")[0].replace("_kccnh", "").replace("_kccnv", "") + imgfilepv[0] = imgfilepv[0].split("_kccxl")[0].replace("_kccnh", "").replace("_kccnv", "") imgfilepv[0] += "_kcchq" imgfilepv = string.join(imgfilepv, ".") else: imgfilepv = imgfile - if "_kccx" in filename[0]: - xy = string.split(filename[0], "_kccx")[1] - x = string.split(xy, "_kccy")[0].lstrip("0") - y = string.split(xy, "_kccy")[1].lstrip("0") - if x != "": - x = "-" + str(float(x)/100) + "%" + if "_kccxl" in filename[0]: + borders = filename[0].split('_kccxl')[1] + borders = re.findall('[0-9]{1,6}', borders) + xl = borders[0].lstrip("0") + yu = borders[1].lstrip("0") + xr = borders[2].lstrip("0") + yd = borders[3].lstrip("0") + if xl != "": + xl = "-" + str(float(xl)/100) + "%" else: - x = "0%" - if y != "": - y = "-" + str(float(y)/100) + "%" + xl = "0%" + if xr != "": + xr = "-" + str(float(xr)/100) + "%" else: - y = "0%" + xr = "0%" + if yu != "": + yu = "-" + str(float(yu)/100) + "%" + else: + yu = "0%" + if yd != "": + yd = "-" + str(float(yd)/100) + "%" + else: + yd = "0%" else: - x = "0%" - y = "0%" - boxStyles = {"BoxTL": "left:" + x + ";top:" + y + ";", - "BoxTR": "right:" + x + ";top:" + y + ";", - "BoxBL": "left:" + x + ";bottom:" + y + ";", - "BoxBR": "right:" + x + ";bottom:" + y + ";", - "BoxT": "left:-25%;top:" + y + ";", - "BoxB": "left:-25%;bottom:" + y + ";", - "BoxL": "left:" + x + ";top:-25%;", - "BoxR": "right:" + x + ";top:-25%;", - "BoxC": "right:-25%;top:-25%;" + xl = "0%" + yu = "0%" + xr = "0%" + yd = "0%" + boxStyles = {"BoxTL": "left:" + xl + ";top:" + yu + ";", + "BoxTR": "right:" + xr + ";top:" + yu + ";", + "BoxBL": "left:" + xl + ";bottom:" + yd + ";", + "BoxBR": "right:" + xr + ";bottom:" + yd + ";", + "BoxT": "left:-25%;top:" + yu + ";", + "BoxB": "left:-25%;bottom:" + yd + ";", + "BoxL": "left:" + xl + ";top:-25%;", + "BoxR": "right:" + xr + ";top:-25%;", + "BoxC": "left:-25%;top:-25%;" } for box in boxes: f.writelines(["
\n", @@ -295,7 +309,7 @@ def getImageFileName(imgfile): return filename -def applyImgOptimization(img, opt, overrideQuality=5): +def applyImgOptimization(img, opt, hqImage=None): if not img.fill: img.getImageFill(opt.webtoon) if not opt.webtoon: @@ -303,10 +317,16 @@ def applyImgOptimization(img, opt, overrideQuality=5): if opt.cutpagenumbers and not opt.webtoon: img.cutPageNumber() img.optimizeImage(opt.gamma) - if overrideQuality != 5: - img.resizeImage(opt.upscale, opt.stretch, opt.bordersColor, overrideQuality) + if hqImage: + img.resizeImage(opt.upscale, opt.stretch, opt.bordersColor, 0) + img.calculateBorder(hqImage, True) else: img.resizeImage(opt.upscale, opt.stretch, opt.bordersColor, opt.quality) + if opt.panelview: + if opt.quality == 0: + img.calculateBorder(img) + elif opt.quality == 1: + img.calculateBorder(img, True) if opt.forcepng and not opt.forcecolor: img.quantizeImage() @@ -378,12 +398,12 @@ def fileImgProcess(work): applyImgOptimization(img1, opt) img1.saveToDir(dirpath, opt.forcepng, opt.forcecolor, wipe) if opt.quality == 2: - img3 = image.ComicPage(split[0], opt.profileData, img0.fill) - applyImgOptimization(img3, opt, 0) - img3.saveToDir(dirpath, opt.forcepng, opt.forcecolor, True) - img4 = image.ComicPage(split[1], opt.profileData, img1.fill) - applyImgOptimization(img4, opt, 0) - img4.saveToDir(dirpath, opt.forcepng, opt.forcecolor, True) + img0b = image.ComicPage(split[0], opt.profileData, img0.fill) + applyImgOptimization(img0b, opt, img0) + img0b.saveToDir(dirpath, opt.forcepng, opt.forcecolor, True) + img1b = image.ComicPage(split[1], opt.profileData, img1.fill) + applyImgOptimization(img1b, opt, img1) + img1b.saveToDir(dirpath, opt.forcepng, opt.forcecolor, True) else: applyImgOptimization(img, opt) img.saveToDir(dirpath, opt.forcepng, opt.forcecolor, wipe) @@ -392,7 +412,7 @@ def fileImgProcess(work): if img.rotated: img2.image = img2.image.rotate(90) img2.rotated = True - applyImgOptimization(img2, opt, 0) + applyImgOptimization(img2, opt, img) img2.saveToDir(dirpath, opt.forcepng, opt.forcecolor, True) except Exception: import traceback diff --git a/kcc/image.py b/kcc/image.py index 1174eb2..a04300c 100755 --- a/kcc/image.py +++ b/kcc/image.py @@ -201,7 +201,8 @@ class ComicPage: if self.noVPV: suffix += "_kccnv" if self.border: - suffix += "_kccx" + str(self.border[0]) + "_kccy" + str(self.border[1]) + suffix += "_kccxl" + str(self.border[0]) + "_kccyu" + str(self.border[1]) + "_kccxr" +\ + str(self.border[2]) + "_kccyd" + str(self.border[3]) if forcepng: self.image.save(os.path.join(targetdir, os.path.splitext(self.filename)[0] + suffix + ".png"), "PNG", optimize=1) @@ -214,6 +215,8 @@ class ComicPage: def optimizeImage(self, gamma): if gamma < 0.1: gamma = self.gamma + if self.gamma != 1.0 and self.isImageColor(self.image): + gamma = 1.0 if gamma == 1.0: self.image = ImageOps.autocontrast(self.image) else: @@ -230,58 +233,61 @@ class ComicPage: # Quantize is deprecated but new function call it internally anyway... self.image = self.image.quantize(palette=palImg) + def calculateBorderPercent(self, x, img, isWidth): + if isWidth: + return int(round(float(x)/float(img.image.size[0]), 4) * 10000 * 1.5) + else: + return int(round(float(x)/float(img.image.size[1]), 4) * 10000 * 1.5) + + def calculateBorder(self, sourceImage, isHQ=False): + if self.fill == 'white': + # This code trigger only when sourceImage is already saved. So we can break color quantization. + if sourceImage.image.mode == 'P': + sourceImage.image = sourceImage.image.convert('RGB') + border = ImageChops.invert(sourceImage.image).getbbox() + else: + border = sourceImage.image.getbbox() + if border is not None: + if isHQ: + multiplier = 1.0 + else: + multiplier = 1.5 + self.border = [self.calculateBorderPercent(border[0], sourceImage, True), + self.calculateBorderPercent(border[1], sourceImage, False), + self.calculateBorderPercent((sourceImage.image.size[0] - border[2]), sourceImage, True), + self.calculateBorderPercent((sourceImage.image.size[1] - border[3]), sourceImage, False)] + if int((border[2] - border[0]) * multiplier) < self.size[0]: + self.noHPV = True + if int((border[3] - border[1]) * multiplier) < self.size[1]: + self.noVPV = True + else: + self.border = [0, 0, 0, 0] + self.noHPV = True + self.noVPV = True + def resizeImage(self, upscale=False, stretch=False, bordersColor=None, qualityMode=0): - # High-quality downscaling filter - method = Image.ANTIALIAS if bordersColor: fill = bordersColor else: fill = self.fill + # Set target size if qualityMode == 0: size = (self.size[0], self.size[1]) - generateBorder = True - elif qualityMode == 1: - size = (self.panelviewsize[0], self.panelviewsize[1]) - generateBorder = True else: size = (self.panelviewsize[0], self.panelviewsize[1]) - generateBorder = False - # If image is smaller than screen and upscale is off - Just expand it - if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1]: - if not upscale: - borderw = (self.size[0] - self.image.size[0]) / 2 - borderh = (self.size[1] - self.image.size[1]) / 2 - self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=fill) - if generateBorder: - if (self.image.size[0]-(2*borderw))*1.5 < self.size[0]: - self.noHPV = True - if (self.image.size[1]-(2*borderh))*1.5 < self.size[1]: - self.noVPV = True - self.border = [int(round(float(borderw)/float(self.image.size[0])*100, 2)*100*1.5), - int(round(float(borderh)/float(self.image.size[1])*100, 2)*100*1.5)] - return self.image - else: - # Cubic spline interpolation in a 4x4 environment - method = Image.BICUBIC + # If image is smaller than device resolution and upscale is off - Just expand it by adding margins + if self.image.size[0] <= self.size[0] and self.image.size[1] <= self.size[1] and not upscale: + borderw = (self.size[0] - self.image.size[0]) / 2 + borderh = (self.size[1] - self.image.size[1]) / 2 + self.image = ImageOps.expand(self.image, border=(borderw, borderh), fill=fill) + return self.image # If stretching is on - Resize without other considerations if stretch: + if self.image.size[0] <= size[0] and self.image.size[1] <= size[1]: + method = Image.BICUBIC + else: + method = Image.ANTIALIAS self.image = self.image.resize(size, method) - if generateBorder: - if fill == 'white': - border = ImageOps.invert(self.image).getbbox() - else: - border = self.image.getbbox() - if border is not None: - if (border[2]-border[0])*1.5 < self.size[0]: - self.noHPV = True - if (border[3]-border[1])*1.5 < self.size[1]: - self.noVPV = True - self.border = [int(round(float(border[0])/float(self.image.size[0])*100, 2)*100*1.5), - int(round(float(border[1])/float(self.image.size[1])*100, 2)*100*1.5)] - else: - self.border = [0, 0] - self.noHPV = True - self.noVPV = True return self.image # Otherwise - Upscale/Downscale ratioDev = float(self.size[0]) / float(self.size[1]) @@ -290,24 +296,12 @@ class ComicPage: self.image = ImageOps.expand(self.image, border=(int(diff / 2), 0), fill=fill) elif (float(self.image.size[0]) / float(self.image.size[1])) > ratioDev: diff = int(self.image.size[0] / ratioDev) - self.image.size[1] - self.image = ImageOps.expand(self.image, border=(0, int(diff / 2)), fill=fill) + self.image = ImageOps.expand(self.image, border=(0, diff / 2), fill=fill) + if self.image.size[0] <= size[0] and self.image.size[1] <= size[1]: + method = Image.BICUBIC + else: + method = Image.ANTIALIAS self.image = ImageOps.fit(self.image, size, method=method, centering=(0.5, 0.5)) - if generateBorder: - if fill == 'white': - border = ImageOps.invert(self.image).getbbox() - else: - border = self.image.getbbox() - if border is not None: - if (border[2]-border[0])*1.5 < self.size[0]: - self.noHPV = True - if (border[3]-border[1])*1.5 < self.size[1]: - self.noVPV = True - self.border = [int(round(float(border[0])/float(self.image.size[0])*100, 2)*100*1.5), - int(round(float(border[1])/float(self.image.size[1])*100, 2)*100*1.5)] - else: - self.border = [0, 0] - self.noHPV = True - self.noVPV = True return self.image def splitPage(self, targetdir, righttoleft=False, rotate=False): @@ -519,4 +513,29 @@ class ComicPage: if fill > 0: self.fill = 'black' else: - self.fill = 'white' \ No newline at end of file + self.fill = 'white' + + def isImageColor(self, image): + v = ImageStat.Stat(image).var + isMonochromatic = reduce(lambda x, y: x and y < 0.005, v, True) + if isMonochromatic: + # Monochromatic + return False + else: + if len(v) == 3: + maxmin = abs(max(v) - min(v)) + if maxmin > 1000: + # Color + return True + elif maxmin > 100: + # Probably color + return True + else: + # Grayscale + return False + elif len(v) == 1: + # Black and white + return False + else: + # Detection failed + return False