mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Do not use QPainter for overlay operations
QPainter requires a QApplication. We do not want to depend on a QApplication in utils/img.py
This commit is contained in:
parent
a9e79dbe57
commit
4ba82e5cea
@ -12,12 +12,18 @@
|
|||||||
|
|
||||||
#define SQUARE(x) (x)*(x)
|
#define SQUARE(x) (x)*(x)
|
||||||
#define MAX(x, y) ((x) > (y)) ? (x) : (y)
|
#define MAX(x, y) ((x) > (y)) ? (x) : (y)
|
||||||
|
#define MIN(x, y) ((x) < (y)) ? (x) : (y)
|
||||||
#define DISTANCE(r, g, b) (SQUARE(r - red_average) + SQUARE(g - green_average) + SQUARE(b - blue_average))
|
#define DISTANCE(r, g, b) (SQUARE(r - red_average) + SQUARE(g - green_average) + SQUARE(b - blue_average))
|
||||||
#define M_EPSILON 1.0e-6
|
#define M_EPSILON 1.0e-6
|
||||||
#define M_SQ2PI 2.50662827463100024161235523934010416269302368164062
|
#define M_SQ2PI 2.50662827463100024161235523934010416269302368164062
|
||||||
#ifndef M_PI
|
#ifndef M_PI
|
||||||
#define M_PI 3.14159265358979323846
|
#define M_PI 3.14159265358979323846
|
||||||
#endif
|
#endif
|
||||||
|
#define ENSURE32(img) \
|
||||||
|
if (img.format() != QImage::Format_RGB32 && img.format() != QImage::Format_ARGB32) { \
|
||||||
|
img = img.convertToFormat(img.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32); \
|
||||||
|
if (img.isNull()) throw std::bad_alloc(); \
|
||||||
|
} \
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@ -54,12 +60,6 @@ unsigned int read_border_row(const QImage &img, const unsigned int width, const
|
|||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ENSURE32(img) \
|
|
||||||
if (img.format() != QImage::Format_RGB32 && img.format() != QImage::Format_ARGB32) { \
|
|
||||||
img = img.convertToFormat(img.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32); \
|
|
||||||
if (img.isNull()) throw std::bad_alloc(); \
|
|
||||||
} \
|
|
||||||
|
|
||||||
QImage remove_borders(const QImage &image, double fuzz) {
|
QImage remove_borders(const QImage &image, double fuzz) {
|
||||||
int *buf = NULL;
|
int *buf = NULL;
|
||||||
QImage img = image, timg;
|
QImage img = image, timg;
|
||||||
@ -576,3 +576,57 @@ QImage despeckle(const QImage &image) {
|
|||||||
return(img);
|
return(img);
|
||||||
}
|
}
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
|
// overlay() {{{
|
||||||
|
static inline unsigned int BYTE_MUL(unsigned int x, unsigned int a) {
|
||||||
|
quint64 t = (((quint64(x)) | ((quint64(x)) << 24)) & 0x00ff00ff00ff00ff) * a;
|
||||||
|
t = (t + ((t >> 8) & 0xff00ff00ff00ff) + 0x80008000800080) >> 8;
|
||||||
|
t &= 0x00ff00ff00ff00ff;
|
||||||
|
return ((unsigned int)(t)) | ((unsigned int)(t >> 24));
|
||||||
|
}
|
||||||
|
|
||||||
|
void overlay(const QImage &image, QImage &canvas, unsigned int left, unsigned int top) {
|
||||||
|
QImage img(image);
|
||||||
|
unsigned int cw = canvas.width(), ch = canvas.height(), iw = img.width(), ih = img.height(), r, c, right = 0, bottom = 0, height, width, s;
|
||||||
|
const QRgb* src;
|
||||||
|
QRgb* dest;
|
||||||
|
|
||||||
|
ENSURE32(canvas)
|
||||||
|
if (canvas.isNull() || cw < 1 || ch < 1) throw std::out_of_range("The canvas cannot be a null image");
|
||||||
|
if (canvas.hasAlphaChannel()) throw std::out_of_range("The canvas must not have transparent pixels");
|
||||||
|
|
||||||
|
left = MIN(cw - 1, left);
|
||||||
|
top = MIN(ch - 1, top);
|
||||||
|
right = MIN(left + iw, cw);
|
||||||
|
bottom = MIN(top + ih, ch);
|
||||||
|
height = bottom - top; width = right - left;
|
||||||
|
|
||||||
|
if (img.hasAlphaChannel()) {
|
||||||
|
if (img.format() != QImage::Format_ARGB32_Premultiplied) {
|
||||||
|
img = img.convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
||||||
|
if (img.isNull()) throw std::bad_alloc();
|
||||||
|
}
|
||||||
|
for (r = 0; r < height; r++) {
|
||||||
|
src = reinterpret_cast<const QRgb*>(img.constScanLine(r));
|
||||||
|
dest = reinterpret_cast<QRgb*>(canvas.scanLine(r + top));
|
||||||
|
for (c = 0; c < width; c++) {
|
||||||
|
// Optimized Alpha blending, taken from qt_blend_argb32_on_argb32
|
||||||
|
// Since the canvas has no transparency
|
||||||
|
// the composite pixel is: canvas*(1-alpha) + src * alpha
|
||||||
|
// but src is pre-multiplied, so it is:
|
||||||
|
// canvas*(1-alpha) + src
|
||||||
|
s = src[c];
|
||||||
|
if (s >= 0xff000000) dest[left+c] = s;
|
||||||
|
else if (s != 0) dest[left+c] = s + BYTE_MUL(dest[left+c], qAlpha(~s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ENSURE32(img);
|
||||||
|
for (r = 0; r < bottom; r++) {
|
||||||
|
src = reinterpret_cast<const QRgb*>(img.constScanLine(r));
|
||||||
|
dest = reinterpret_cast<QRgb*>(canvas.scanLine(r + top));
|
||||||
|
memcpy(dest + left, src, (right - left) * sizeof(QRgb));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // }}}
|
||||||
|
@ -15,4 +15,5 @@ QImage grayscale(const QImage &image);
|
|||||||
QImage gaussian_sharpen(const QImage &img, const float radius, const float sigma, const bool high_quality=true);
|
QImage gaussian_sharpen(const QImage &img, const float radius, const float sigma, const bool high_quality=true);
|
||||||
QImage gaussian_blur(const QImage &img, const float radius, const float sigma);
|
QImage gaussian_blur(const QImage &img, const float radius, const float sigma);
|
||||||
QImage despeckle(const QImage &image);
|
QImage despeckle(const QImage &image);
|
||||||
|
void overlay(const QImage &image, QImage &canvas, unsigned int left, unsigned int top);
|
||||||
|
|
||||||
|
@ -51,3 +51,10 @@ QImage despeckle(const QImage &image);
|
|||||||
sipRes = new QImage(despeckle(*a0));
|
sipRes = new QImage(despeckle(*a0));
|
||||||
IMAGEOPS_SUFFIX
|
IMAGEOPS_SUFFIX
|
||||||
%End
|
%End
|
||||||
|
|
||||||
|
void overlay(const QImage &image, QImage &canvas, unsigned int left, unsigned int top);
|
||||||
|
%MethodCode
|
||||||
|
IMAGEOPS_PREFIX
|
||||||
|
overlay(*a0, *a1, a2, a3);
|
||||||
|
IMAGEOPS_SUFFIX
|
||||||
|
%End
|
||||||
|
@ -7,7 +7,7 @@ from __future__ import (unicode_literals, division, absolute_import,
|
|||||||
import os, subprocess, errno, shutil, tempfile
|
import os, subprocess, errno, shutil, tempfile
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
from PyQt5.Qt import QImage, QByteArray, QBuffer, Qt, QPainter, QImageReader, QColor
|
from PyQt5.Qt import QImage, QByteArray, QBuffer, Qt, QImageReader, QColor
|
||||||
|
|
||||||
from calibre import fit_image, force_unicode
|
from calibre import fit_image, force_unicode
|
||||||
from calibre.constants import iswindows, plugins
|
from calibre.constants import iswindows, plugins
|
||||||
@ -54,19 +54,32 @@ def image_and_format_from_data(data):
|
|||||||
return r.read(), fmt
|
return r.read(), fmt
|
||||||
|
|
||||||
def add_borders(img, left=0, top=0, right=0, bottom=0, border_color='#ffffff'):
|
def add_borders(img, left=0, top=0, right=0, bottom=0, border_color='#ffffff'):
|
||||||
|
if not (left > 0 or right > 0 or top > 0 or bottom > 0):
|
||||||
|
return img
|
||||||
nimg = QImage(img.width() + left + right, img.height() + top + bottom, QImage.Format_RGB32)
|
nimg = QImage(img.width() + left + right, img.height() + top + bottom, QImage.Format_RGB32)
|
||||||
nimg.fill(QColor(border_color))
|
nimg.fill(QColor(border_color))
|
||||||
p = QPainter(nimg)
|
overlay(img, nimg, left, top)
|
||||||
p.drawImage(left, top, img)
|
|
||||||
p.end()
|
|
||||||
return nimg
|
return nimg
|
||||||
|
|
||||||
|
def overlay(img, canvas=None, left=0, top=0):
|
||||||
|
if canvas is None:
|
||||||
|
canvas = QImage(img.size(), QImage.Format_RGB32)
|
||||||
|
canvas.fill(Qt.white)
|
||||||
|
if imageops is None:
|
||||||
|
from PyQt5.Qt import QPainter
|
||||||
|
from calibre.gui2 import ensure_app
|
||||||
|
ensure_app()
|
||||||
|
p = QPainter(canvas)
|
||||||
|
p.drawImage(left, top, img)
|
||||||
|
p.end()
|
||||||
|
else:
|
||||||
|
imageops.overlay(img, canvas, left, top)
|
||||||
|
return canvas
|
||||||
|
|
||||||
def blend_image(img, bgcolor='#ffffff'):
|
def blend_image(img, bgcolor='#ffffff'):
|
||||||
nimg = QImage(img.size(), QImage.Format_RGB32)
|
nimg = QImage(img.size(), QImage.Format_RGB32)
|
||||||
nimg.fill(QColor(bgcolor))
|
nimg.fill(QColor(bgcolor))
|
||||||
p = QPainter(nimg)
|
overlay(img, nimg)
|
||||||
p.drawImage(0, 0, img)
|
|
||||||
p.end()
|
|
||||||
return nimg
|
return nimg
|
||||||
|
|
||||||
def image_to_data(img, compression_quality=95, fmt='JPEG'):
|
def image_to_data(img, compression_quality=95, fmt='JPEG'):
|
||||||
@ -178,9 +191,7 @@ def blend_on_canvas(img, width, height, bgcolor='#ffffff'):
|
|||||||
w, h = nw, nh
|
w, h = nw, nh
|
||||||
nimg = QImage(width, height, QImage.Format_RGB32)
|
nimg = QImage(width, height, QImage.Format_RGB32)
|
||||||
nimg.fill(QColor(bgcolor))
|
nimg.fill(QColor(bgcolor))
|
||||||
p = QPainter(nimg)
|
overlay(img, nimg, (width - w)//2, (height - h)//2)
|
||||||
p.drawImage((width - w)//2, (height - h)//2, img)
|
|
||||||
p.end()
|
|
||||||
return nimg
|
return nimg
|
||||||
|
|
||||||
class Canvas(object):
|
class Canvas(object):
|
||||||
@ -190,15 +201,14 @@ class Canvas(object):
|
|||||||
self.img.fill(QColor(bgcolor))
|
self.img.fill(QColor(bgcolor))
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self.painter = QPainter(self.img)
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args):
|
||||||
self.painter.end()
|
pass
|
||||||
|
|
||||||
def compose(self, img, x=0, y=0):
|
def compose(self, img, x=0, y=0):
|
||||||
img = image_from_data(img)
|
img = image_from_data(img)
|
||||||
self.painter.drawImage(x, y, img)
|
overlay(img, self.img, x, y)
|
||||||
|
|
||||||
def export(self, fmt='JPEG', compression_quality=95):
|
def export(self, fmt='JPEG', compression_quality=95):
|
||||||
return image_to_data(self.img, compression_quality=compression_quality, fmt=fmt)
|
return image_to_data(self.img, compression_quality=compression_quality, fmt=fmt)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user