This repository has been archived on 2025-02-01. You can view files and clone it, but cannot push or open issues or pull requests.
PyBitmessage-2025-02-01/mockenv/lib/python3.6/site-packages/qrcode/image/styles/moduledrawers.py

233 lines
10 KiB
Python
Raw Normal View History

2022-07-22 12:43:59 +02:00
# Needed on case-insensitive filesystems
from __future__ import absolute_import
# Try to import PIL in either of the two ways it can be installed.
try:
from PIL import Image, ImageDraw
except ImportError: # pragma: no cover
import Image
import ImageDraw
# When drawing antialiased things, make them bigger and then shrink them down to size after the geometry has been drawn
ANTIALIASING_FACTOR = 4
class QRModuleDrawer:
"""
QRModuleDrawer exists to draw the modules of the QR Code onto a PIL image
For this, technically all that is necessary is a drawrect_context(self, box, is_active, context) function
which takes in the box in which it is to draw, whether or not the box is "active" (a module exists there),
and the context (the neighboring pixels)
It is frequently necessary to also implement an "initialize" function to set up values that only the containing StyledPilImage
knows about.
NOTE: the color that this draws in should be whatever is equivalent to black in the color space, and the specified QRColorMask
will handle adding colors as necessary to the image
For examples of what these look like, see doc/module_drawers.png
"""
fill = None
def initialize(self, styledPilImage, image):
self.fill = styledPilImage.paint_color
def drawrect_context(self, box, is_active, context):
raise NotImplementedError("QRModuleDrawer.drawrect_context")
# helper for figuring out the context, which is an array containing information on neighboring pixels
# I refer to these by their cardinal directions, like:
# [NW, N, NE,
# W, E,
# SW, S, SE]
DIRECTIONS = {
'NW': 0,
'N': 1,
'NE': 2,
'W': 3,
'E': 4,
'SW': 5,
'S': 6,
'SE': 7
}
def get(self, context, direction):
return context[self.DIRECTIONS[direction]]
class SquareModuleDrawer(QRModuleDrawer):
"""
Draws the modules as simple squares
"""
fill = None
def initialize(self, styledPilImage, image):
self.imgDraw = ImageDraw.Draw(image)
self.fill = styledPilImage.paint_color
def drawrect_context(self, box, is_active, context):
if is_active:
self.imgDraw.rectangle(box, fill=self.fill)
class GappedSquareModuleDrawer(QRModuleDrawer):
"""
Draws the modules as simple squares that are not contiguous.
The size_ratio determines how wide the squares are relative to the width of the space they are printed in
"""
fill = None
def __init__(self, size_ratio = 0.8):
self.size_ratio = size_ratio
def initialize(self, styledPilImage, image):
self.imgDraw = ImageDraw.Draw(image)
self.fill = styledPilImage.paint_color
self.delta = (1 - self.size_ratio) * styledPilImage.box_size / 2
def drawrect_context(self, box, is_active, context):
if is_active:
smaller_box = (
box[0][0] + self.delta,
box[0][1] + self.delta,
box[1][0] - self.delta,
box[1][1] - self.delta
)
self.imgDraw.rectangle(smaller_box, fill=self.fill)
class CircleModuleDrawer(QRModuleDrawer):
"""
Draws the modules as circles
"""
circle = None
def initialize(self, styledPilImage, image):
box_size = styledPilImage.box_size
fake_size = box_size * ANTIALIASING_FACTOR
self.circle = Image.new(styledPilImage.mode,(fake_size, fake_size), styledPilImage.color_mask.back_color)
ImageDraw.Draw(self.circle).ellipse((0,0, fake_size, fake_size), fill = styledPilImage.paint_color)
self.circle = self.circle.resize((box_size, box_size), Image.LANCZOS)
self.image = image
def drawrect_context(self, box, is_active, context):
if is_active:
self.image.paste(self.circle, (box[0][0], box[0][1]))
class RoundedModuleDrawer(QRModuleDrawer):
"""
Draws the modules with all 90 degree corners replaced with rounded edges
radius_ratio determines the radius of the rounded edges -
a value of 1 means that an isolated module will be drawn as a circle,
while a value of 0 means that the radius of the rounded edge will be 0 (and thus back to 90 degrees again)
"""
def __init__(self, radius_ratio = 1):
self.radius_ratio = radius_ratio
def initialize(self, styledPilImage, image):
self.corner_width = int(styledPilImage.box_size / 2)
self.image = image
self.setup_corners(styledPilImage.mode, styledPilImage.color_mask.back_color, styledPilImage.paint_color)
def setup_corners(self, mode, back_color, front_color):
self.SQUARE = Image.new(mode, (self.corner_width, self.corner_width), front_color)
fake_width = self.corner_width * ANTIALIASING_FACTOR
radius = self.radius_ratio * fake_width
diameter = radius * 2
base = Image.new(mode, (fake_width, fake_width), back_color) # make something 4x bigger for antialiasing
base_draw = ImageDraw.Draw(base)
base_draw.ellipse((0,0, diameter, diameter), fill = front_color)
base_draw.rectangle((radius, 0, fake_width, fake_width), fill = front_color)
base_draw.rectangle((0, radius, fake_width, fake_width), fill = front_color)
self.NW_ROUND = base.resize((self.corner_width, self.corner_width), Image.LANCZOS)
self.SW_ROUND = self.NW_ROUND.transpose(Image.FLIP_TOP_BOTTOM)
self.SE_ROUND = self.NW_ROUND.transpose(Image.ROTATE_180)
self.NE_ROUND = self.NW_ROUND.transpose(Image.FLIP_LEFT_RIGHT)
def drawrect_context(self, box, is_active, context):
if not is_active:
return
# find rounded edges
nw_rounded = not self.get(context, 'W') and not self.get(context, 'N')
ne_rounded = not self.get(context, 'N') and not self.get(context, 'E')
se_rounded = not self.get(context, 'E') and not self.get(context, 'S')
sw_rounded = not self.get(context, 'S') and not self.get(context, 'W')
nw = self.NW_ROUND if nw_rounded else self.SQUARE
ne = self.NE_ROUND if ne_rounded else self.SQUARE
se = self.SE_ROUND if se_rounded else self.SQUARE
sw = self.SW_ROUND if sw_rounded else self.SQUARE
self.image.paste(nw, (box[0][0], box[0][1]))
self.image.paste(ne, (box[0][0] + self.corner_width, box[0][1]))
self.image.paste(se, (box[0][0] + self.corner_width, box[0][1] + self.corner_width))
self.image.paste(sw, (box[0][0], box[0][1] + self.corner_width))
class VerticalBarsDrawer(QRModuleDrawer):
"""
Draws vertically contiguous groups of modules as long rounded rectangles, with gaps between neighboring bands
(the size of these gaps is inversely proportional to the horizontal_shrink)
"""
def __init__(self, horizontal_shrink = 0.8):
self.horizontal_shrink = horizontal_shrink
def initialize(self, styledPilImage, image):
self.half_height = int(styledPilImage.box_size / 2)
self.image = image
self.delta = int((1-self.horizontal_shrink) * self.half_height)
self.setup_edges(styledPilImage.mode, styledPilImage.color_mask.back_color, styledPilImage.paint_color)
def setup_edges(self, mode, back_color, front_color):
height = self.half_height
width = height * 2
shrunken_width = int(width * self.horizontal_shrink)
self.SQUARE = Image.new(mode, (shrunken_width, height), front_color)
fake_width = width * ANTIALIASING_FACTOR
fake_height = height * ANTIALIASING_FACTOR
radius = fake_width
base = Image.new(mode, (fake_width, fake_height), back_color) # make something 4x bigger for antialiasing
base_draw = ImageDraw.Draw(base)
base_draw.ellipse((0,0, fake_width, fake_height * 2), fill = front_color)
self.ROUND_TOP = base.resize((shrunken_width, height), Image.LANCZOS)
self.ROUND_BOTTOM = self.ROUND_TOP.transpose(Image.FLIP_TOP_BOTTOM)
def drawrect_context(self, box, is_active, context):
if is_active:
# find rounded edges
top_rounded = not self.get(context, 'N')
bottom_rounded = not self.get(context, 'S')
top = self.ROUND_TOP if top_rounded else self.SQUARE
bottom = self.ROUND_BOTTOM if bottom_rounded else self.SQUARE
self.image.paste(top, (box[0][0] + self.delta, box[0][1]))
self.image.paste(bottom, (box[0][0] + self.delta, box[0][1] + self.half_height))
class HorizontalBarsDrawer(QRModuleDrawer):
"""
Draws horizontally contiguous groups of modules as long rounded rectangles, with gaps between neighboring bands
(the size of these gaps is inversely proportional to the vertical_shrink)
"""
def __init__(self, vertical_shrink = 0.8):
self.vertical_shrink = vertical_shrink
def initialize(self, styledPilImage, image):
self.half_width = int(styledPilImage.box_size / 2)
self.image = image
self.delta = int((1-self.vertical_shrink) * self.half_width)
self.setup_edges(styledPilImage.mode, styledPilImage.color_mask.back_color, styledPilImage.paint_color)
def setup_edges(self, mode, back_color, front_color):
width = self.half_width
height = width * 2
shrunken_height= int(height * self.vertical_shrink)
self.SQUARE = Image.new(mode, (width, shrunken_height), front_color)
fake_width = width * ANTIALIASING_FACTOR
fake_height = height * ANTIALIASING_FACTOR
radius = fake_height
base = Image.new(mode, (fake_width, fake_height), back_color) # make something 4x bigger for antialiasing
base_draw = ImageDraw.Draw(base)
base_draw.ellipse((0,0, fake_width * 2, fake_height), fill = front_color)
self.ROUND_LEFT = base.resize((width, shrunken_height), Image.LANCZOS)
self.ROUND_RIGHT = self.ROUND_LEFT.transpose(Image.FLIP_LEFT_RIGHT)
def drawrect_context(self, box, is_active, context):
if is_active:
# find rounded edges
left_rounded = not self.get(context, 'W')
right_rounded = not self.get(context, 'E')
left = self.ROUND_LEFT if left_rounded else self.SQUARE
right = self.ROUND_RIGHT if right_rounded else self.SQUARE
self.image.paste(left, (box[0][0], box[0][1] + self.delta))
self.image.paste(right, (box[0][0] + self.half_width, box[0][1] + self.delta))