mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 10:14:46 -04:00
Add linear gradient code but leave it disabled as it's much worse quality
This commit is contained in:
parent
a24aef097b
commit
dbbde02e0a
@ -82,7 +82,7 @@ class PdfEngine(QPaintEngine):
|
||||
self.bottom_margin) / self.pixel_height
|
||||
|
||||
self.pdf_system = QTransform(sx, 0, 0, -sy, dx, dy)
|
||||
self.graphics = Graphics()
|
||||
self.graphics = Graphics(self.pixel_width, self.pixel_height)
|
||||
self.errors_occurred = False
|
||||
self.errors, self.debug = errors, debug
|
||||
self.fonts = {}
|
||||
|
@ -7,20 +7,179 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import sys
|
||||
from math import floor, ceil
|
||||
from future_builtins import map
|
||||
|
||||
from PyQt4.Qt import (QPointF)
|
||||
import sip
|
||||
from PyQt4.Qt import (QPointF, QGradient, QLinearGradient)
|
||||
|
||||
from calibre.ebooks.pdf.render.common import Stream
|
||||
from calibre.ebooks.pdf.render.common import Stream, Name, Array, Dictionary
|
||||
|
||||
def write_triple(data, val):
|
||||
data.write(bytes(bytearray((
|
||||
(val >> 16) & 0xff, (val >> 8) & 0xff, val & 0xff))))
|
||||
|
||||
def write_byte(data, val):
|
||||
data.write(bytes(bytearray([val&0xff])))
|
||||
|
||||
def write_triangle_line(data, xpos, ypos, xoff, yoff, rgb, flag, alpha):
|
||||
for xo, yo in ( (0, 0), (xoff, yoff) ):
|
||||
write_byte(data, flag)
|
||||
write_triple(data, xpos+xo)
|
||||
write_triple(data, ypos+yo)
|
||||
if alpha:
|
||||
write_byte(data, rgb[-1])
|
||||
else:
|
||||
for x in rgb[:3]:
|
||||
write_byte(data, x)
|
||||
|
||||
class LinearShader(Stream):
|
||||
|
||||
def __init__(self, is_transparent, xmin, xmax, ymin, ymax):
|
||||
Stream.__init__(self, compress=False)
|
||||
self.is_transparent = is_transparent
|
||||
self.xmin, self.xmax, self.ymin, self.ymax = (xmin, xmax, ymin, ymax)
|
||||
self.cache_key = None
|
||||
|
||||
def add_extra_keys(self, d):
|
||||
d['ShadingType'] = 4
|
||||
d['ColorSpace'] = Name('DeviceGray' if self.is_transparent else 'DeviceRGB')
|
||||
d['AntiAlias'] = True
|
||||
d['BitsPerCoordinate'] = 24
|
||||
d['BitsPerComponent'] = 8
|
||||
d['BitsPerFlag'] = 8
|
||||
a = ([0, 1] if self.is_transparent else [0, 1, 0, 1, 0, 1])
|
||||
d['Decode'] = Array([self.xmin, self.xmax, self.ymin, self.ymax]+a)
|
||||
d['AntiAlias'] = True
|
||||
|
||||
def generate_linear_gradient_shader(gradient, page_rect, is_transparent=False):
|
||||
pass
|
||||
start = gradient.start()
|
||||
stop = gradient.finalStop()
|
||||
stops = list(map(list, gradient.stops()))
|
||||
offset = stop - start
|
||||
spread = gradient.spread()
|
||||
|
||||
class LinearGradient(Stream):
|
||||
if gradient.spread() == QGradient.ReflectSpread:
|
||||
offset *= 2
|
||||
for i in xrange(len(stops) - 2, -1, -1):
|
||||
s = stops[i]
|
||||
s[0] = 2. - s[0]
|
||||
stops.append(s)
|
||||
for i in xrange(len(stops)):
|
||||
stops[i][0] /= 2.
|
||||
|
||||
def __init__(self, brush, matrix, pixel_page_width, pixel_page_height):
|
||||
is_opaque = brush.isOpaque()
|
||||
gradient = brush.gradient()
|
||||
orthogonal = QPointF(offset.y(), -offset.x())
|
||||
length = offset.x()*offset.x() + offset.y()*offset.y()
|
||||
|
||||
# find the max and min values in offset and orth direction that are needed to cover
|
||||
# the whole page
|
||||
off_min = sys.maxint
|
||||
off_max = -sys.maxint - 1
|
||||
ort_min = sys.maxint
|
||||
ort_max = -sys.maxint - 1
|
||||
for i in xrange(4):
|
||||
off = ((page_rect[i].x() - start.x()) * offset.x() + (page_rect[i].y() - start.y()) * offset.y())/length
|
||||
ort = ((page_rect[i].x() - start.x()) * orthogonal.x() + (page_rect[i].y() - start.y()) * orthogonal.y())/length
|
||||
off_min = min(off_min, int(floor(off)))
|
||||
off_max = max(off_max, int(ceil(off)))
|
||||
ort_min = min(ort_min, ort)
|
||||
ort_max = max(ort_max, ort)
|
||||
ort_min -= 1
|
||||
ort_max += 1
|
||||
|
||||
start += off_min * offset + ort_min * orthogonal
|
||||
orthogonal *= (ort_max - ort_min)
|
||||
num = off_max - off_min
|
||||
|
||||
gradient_rect = [ start, start + orthogonal, start + num*offset, start +
|
||||
num*offset + orthogonal]
|
||||
|
||||
xmin = gradient_rect[0].x()
|
||||
xmax = gradient_rect[0].x()
|
||||
ymin = gradient_rect[0].y()
|
||||
ymax = gradient_rect[0].y()
|
||||
for i in xrange(1, 4):
|
||||
xmin = min(xmin, gradient_rect[i].x())
|
||||
xmax = max(xmax, gradient_rect[i].x())
|
||||
ymin = min(ymin, gradient_rect[i].y())
|
||||
ymax = max(ymax, gradient_rect[i].y())
|
||||
xmin -= 1000
|
||||
xmax += 1000
|
||||
ymin -= 1000
|
||||
ymax += 1000
|
||||
start -= QPointF(xmin, ymin)
|
||||
factor_x = float(1<<24)/(xmax - xmin)
|
||||
factor_y = float(1<<24)/(ymax - ymin)
|
||||
xoff = int(orthogonal.x()*factor_x)
|
||||
yoff = int(orthogonal.y()*factor_y)
|
||||
|
||||
triangles = LinearShader(is_transparent, xmin, xmax, ymin, ymax)
|
||||
if spread == QGradient.PadSpread:
|
||||
if (off_min > 0 or off_max < 1):
|
||||
# linear gradient outside of page
|
||||
current_stop = stops[len(stops)-1] if off_min > 0 else stops[0]
|
||||
rgb = current_stop[1].getRgb()
|
||||
xpos = int(start.x()*factor_x)
|
||||
ypos = int(start.y()*factor_y)
|
||||
write_triangle_line(triangles, xpos, ypos, xoff, yoff, rgb, 0,
|
||||
is_transparent)
|
||||
start += num*offset
|
||||
xpos = int(start.x()*factor_x)
|
||||
ypos = int(start.y()*factor_y)
|
||||
write_triangle_line(triangles, xpos, ypos, xoff, yoff, rgb, 1,
|
||||
is_transparent)
|
||||
else:
|
||||
flag = 0
|
||||
if off_min < 0:
|
||||
rgb = stops[0][1].getRgb()
|
||||
xpos = int(start.x()*factor_x)
|
||||
ypos = int(start.y()*factor_y)
|
||||
write_triangle_line(triangles, xpos, ypos, xoff, yoff, rgb, flag,
|
||||
is_transparent)
|
||||
start -= off_min*offset
|
||||
flag = 1
|
||||
for s, current_stop in enumerate(stops):
|
||||
rgb = current_stop[1].getRgb()
|
||||
xpos = int(start.x()*factor_x)
|
||||
ypos = int(start.y()*factor_y)
|
||||
write_triangle_line(triangles, xpos, ypos, xoff, yoff, rgb, flag,
|
||||
is_transparent)
|
||||
if s < len(stops)-1:
|
||||
start += offset*(stops[s+1][0] - stops[s][0])
|
||||
flag = 1
|
||||
|
||||
if off_max > 1:
|
||||
start += (off_max - 1)*offset
|
||||
rgb = stops[len(stops)-1][1].getRgb()
|
||||
xpos = int(start.x()*factor_x)
|
||||
ypos = int(start.y()*factor_y)
|
||||
write_triangle_line(triangles, xpos, ypos, xoff, yoff, rgb, flag,
|
||||
is_transparent);
|
||||
|
||||
else:
|
||||
for i in xrange(num):
|
||||
flag = 0
|
||||
for s in xrange(len(stops)):
|
||||
rgb = stops[s][1].getRgb()
|
||||
xpos = int(start.x()*factor_x)
|
||||
ypos = int(start.y()*factor_y)
|
||||
write_triangle_line(triangles, xpos, ypos, xoff, yoff, rgb, flag,
|
||||
is_transparent)
|
||||
if s < len(stops)-1:
|
||||
start += offset*(stops[s+1][0] - stops[s][0])
|
||||
flag = 1
|
||||
|
||||
t = triangles
|
||||
t.cache_key = (t.xmin, t.xmax, t.ymin, t.ymax, t.is_transparent, hash(t.getvalue()))
|
||||
return triangles
|
||||
|
||||
class LinearGradientPattern(Dictionary):
|
||||
|
||||
def __init__(self, brush, matrix, pdf, pixel_page_width, pixel_page_height):
|
||||
self.matrix = (matrix.m11(), matrix.m12(), matrix.m21(), matrix.m22(),
|
||||
matrix.dx(), matrix.dy())
|
||||
gradient = sip.cast(brush.gradient(), QLinearGradient)
|
||||
inv = matrix.inverted()[0]
|
||||
|
||||
page_rect = tuple(map(inv.map, (
|
||||
@ -28,10 +187,20 @@ class LinearGradient(Stream):
|
||||
QPointF(pixel_page_width, pixel_page_height))))
|
||||
|
||||
shader = generate_linear_gradient_shader(gradient, page_rect)
|
||||
alpha_shader = None
|
||||
if not is_opaque:
|
||||
alpha_shader = generate_linear_gradient_shader(gradient, page_rect, True)
|
||||
self.const_opacity = 1.0
|
||||
if not brush.isOpaque():
|
||||
# TODO: Handle colors with different opacities in the gradient
|
||||
self.const_opacity = gradient.stops()[0][1].alphaF()
|
||||
|
||||
shader, alpha_shader
|
||||
self.shaderref = pdf.add_shader(shader)
|
||||
|
||||
d = {}
|
||||
d['Type'] = Name('Pattern')
|
||||
d['PatternType'] = 2
|
||||
d['Shading'] = self.shaderref
|
||||
d['Matrix'] = Array(self.matrix)
|
||||
Dictionary.__init__(self, d)
|
||||
|
||||
self.cache_key = (self.__class__.__name__, self.matrix,
|
||||
repr(self.shaderref))
|
||||
|
||||
|
@ -16,6 +16,7 @@ from PyQt4.Qt import (
|
||||
from calibre.ebooks.pdf.render.common import (
|
||||
Name, Array, fmtnum, Stream, Dictionary)
|
||||
from calibre.ebooks.pdf.render.serialize import Path
|
||||
from calibre.ebooks.pdf.render.gradients import LinearGradientPattern
|
||||
|
||||
def convert_path(path): # {{{
|
||||
p = Path()
|
||||
@ -280,10 +281,11 @@ class GraphicsState(object):
|
||||
|
||||
class Graphics(object):
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, page_width_px, page_height_px):
|
||||
self.base_state = GraphicsState()
|
||||
self.current_state = GraphicsState()
|
||||
self.pending_state = None
|
||||
self.page_width_px, self.page_height_px = (page_width_px, page_height_px)
|
||||
|
||||
def begin(self, pdf):
|
||||
self.pdf = pdf
|
||||
@ -360,7 +362,7 @@ class Graphics(object):
|
||||
pdf = self.pdf
|
||||
|
||||
pattern = color = pat = None
|
||||
opacity = 1.0
|
||||
opacity = global_opacity
|
||||
do_fill = True
|
||||
|
||||
matrix = (QTransform.fromTranslate(brush_origin.x(), brush_origin.y())
|
||||
@ -369,29 +371,30 @@ class Graphics(object):
|
||||
self.brushobj = None
|
||||
|
||||
if style <= Qt.DiagCrossPattern:
|
||||
opacity = global_opacity * vals[-1]
|
||||
opacity *= vals[-1]
|
||||
color = vals[:3]
|
||||
|
||||
if style > Qt.SolidPattern:
|
||||
pat = QtPattern(style, matrix)
|
||||
pattern = pdf.add_pattern(pat)
|
||||
|
||||
if opacity < 1e-4 or style == Qt.NoBrush:
|
||||
do_fill = False
|
||||
|
||||
elif style == Qt.TexturePattern:
|
||||
pat = TexturePattern(brush.texture(), matrix, pdf)
|
||||
opacity = global_opacity
|
||||
if pat.paint_type == 2:
|
||||
opacity *= vals[-1]
|
||||
color = vals[:3]
|
||||
pattern = pdf.add_pattern(pat)
|
||||
|
||||
if opacity < 1e-4 or style == Qt.NoBrush:
|
||||
do_fill = False
|
||||
elif False and style == Qt.LinearGradientPattern:
|
||||
pat = LinearGradientPattern(brush, matrix, pdf, self.page_width_px,
|
||||
self.page_height_px)
|
||||
opacity *= pat.const_opacity
|
||||
# TODO: Add support for radial/conical gradient fills
|
||||
|
||||
if opacity < 1e-4 or style == Qt.NoBrush:
|
||||
do_fill = False
|
||||
self.brushobj = Brush(brush_origin, pat, color)
|
||||
# TODO: Add support for gradient fills
|
||||
|
||||
if pat is not None:
|
||||
pattern = pdf.add_pattern(pat)
|
||||
return color, opacity, pattern, do_fill
|
||||
|
||||
def apply_stroke(self, state, pdf_system, painter):
|
||||
|
@ -264,7 +264,7 @@ class PDFStream(object):
|
||||
self.stroke_opacities, self.fill_opacities = {}, {}
|
||||
self.font_manager = FontManager(self.objects, self.compress)
|
||||
self.image_cache = {}
|
||||
self.pattern_cache = {}
|
||||
self.pattern_cache, self.shader_cache = {}, {}
|
||||
self.debug = debug
|
||||
self.links = Links(self, mark_links, page_size)
|
||||
i = QImage(1, 1, QImage.Format_ARGB32)
|
||||
@ -447,6 +447,11 @@ class PDFStream(object):
|
||||
self.pattern_cache[pattern.cache_key] = self.objects.add(pattern)
|
||||
return self.current_page.add_pattern(self.pattern_cache[pattern.cache_key])
|
||||
|
||||
def add_shader(self, shader):
|
||||
if shader.cache_key not in self.shader_cache:
|
||||
self.shader_cache[shader.cache_key] = self.objects.add(shader)
|
||||
return self.shader_cache[shader.cache_key]
|
||||
|
||||
def draw_image(self, x, y, width, height, imgref):
|
||||
name = self.current_page.add_image(imgref)
|
||||
self.current_page.write('q %s 0 0 %s %s %s cm '%(fmtnum(width),
|
||||
|
@ -86,10 +86,10 @@ def brush(p, xmax, ymax):
|
||||
x = xmax/3
|
||||
y = 0
|
||||
w = xmax/2
|
||||
pix = QPixmap(I('console.png'))
|
||||
p.fillRect(x, y, w, w, QBrush(pix))
|
||||
|
||||
p.fillRect(0, y+xmax/1.9, w, w, QBrush(pix))
|
||||
g = QLinearGradient(QPointF(x, y), QPointF(x+w, y+w))
|
||||
g.setColorAt(0, QColor('#00f'))
|
||||
g.setColorAt(1, QColor('#fff'))
|
||||
p.fillRect(x, y, w, w, QBrush(g))
|
||||
|
||||
def pen(p, xmax, ymax):
|
||||
pix = QPixmap(I('console.png'))
|
||||
@ -110,7 +110,7 @@ def main():
|
||||
app
|
||||
tdir = os.path.abspath('.')
|
||||
pdf = os.path.join(tdir, 'painter.pdf')
|
||||
func = full
|
||||
func = brush
|
||||
dpi = 100
|
||||
with open(pdf, 'wb') as f:
|
||||
dev = PdfDevice(f, xdpi=dpi, ydpi=dpi, compress=False)
|
||||
|
Loading…
x
Reference in New Issue
Block a user