diff --git a/app/forms.py b/app/forms.py index ad6c87d..ac45099 100644 --- a/app/forms.py +++ b/app/forms.py @@ -24,6 +24,8 @@ class DisplayEditFormImpl(FlaskForm): name = StringField('Display Name', validators=[DataRequired()]) if current_app.config['ENABLE_DISPLAY_APPROVAL']: form_status = SelectField("Status", choices=[(k, v) for k, v in DISP_STATUS.items()], validators=[DataRequired()]) + image_format = SelectField("Image Format", choices=[('BMP', 'BMP'), ('JPEG', 'JPEG'), ('PNG', 'PNG')], validators=[DataRequired()]) + image_bit_depth = SelectField("Image Bit Depth", choices=[(None, 'Default'), (1, '1 bit (monochrome)'), (16, '16 bit'), (24, '24 bit')], validators=[Optional()]) playlist = QuerySelectField('Playlist', validators=[Optional()], query_factory=lambda: Playlist.query.order_by(Playlist.name.asc()), diff --git a/app/lib/image.py b/app/lib/image.py index 37859b3..0187558 100644 --- a/app/lib/image.py +++ b/app/lib/image.py @@ -1,4 +1,4 @@ -from typing import List, Tuple +from typing import List, Tuple, Optional from PIL import Image @@ -19,11 +19,10 @@ def convert_palette(palette: List[int]) -> List[int]: return out -def convert_colors(color_spec: str, input_path: str): +def convert_colors__cs(color_spec: str, in_im): + """Initial conversion to what's specified in color_spec""" cs = COLOR_SPEC.get(color_spec, COLOR_SPEC['1b']) - in_im = Image.open(input_path).convert('RGB') - if cs['bits'] == 1: mode = '1' elif cs['bits'] < 16: @@ -52,3 +51,25 @@ def convert_colors(color_spec: str, input_path: str): out_im.paste(in_im, tuple([0, 0] + list(in_im.size))) return out_im + + +def convert_colors__bits(bit_depth: Optional[int], in_im): + """Second conversion to actual bit depth""" + if bit_depth: + if bit_depth >= 16: + return in_im.convert('RGB') + else: + # must be 1, 16, 24 - this case would be 1 bit + out_im = Image.new('1', in_im.size) + in_im = in_im.convert('L') + in_im = in_im.point(lambda p: 255 if p >= 170 else 0) + out_im.paste(in_im, tuple([0, 0] + list(in_im.size))) + return out_im + return in_im + + +def convert_colors(bit_depth: Optional[int], color_spec: str, input_path: str): + im = Image.open(input_path).convert('RGB') + im = convert_colors__cs(color_spec, im) + im = convert_colors__bits(bit_depth, im) + return im diff --git a/app/lib/jinja.py b/app/lib/jinja.py index 2e7611c..8a382dd 100644 --- a/app/lib/jinja.py +++ b/app/lib/jinja.py @@ -122,6 +122,31 @@ def color_spec(value): return label(cs['name'], level='default') +@jfilter() +def image_format(value): + level = 'default' + if value == 'BMP': + level = 'info' + if value == 'JPEG': + level = 'primary' + if value == 'PNG': + level = 'success' + return label(value, level=level) + + +@jfilter() +def image_bit_depth(value): + level = 'default' + if value is not None: + if value >= 24: + level = 'success' + elif value >= 16: + level = 'info' + elif value >= 1: + level = 'primary' + return label(value or 'Default', level=level) + + @jfilter() def fixed(value, d=2): if value is not None: diff --git a/app/models.py b/app/models.py index cfcd18c..20cb8b5 100644 --- a/app/models.py +++ b/app/models.py @@ -319,6 +319,8 @@ class Display(Base): last_seen_at = db.Column(sau.ArrowType(), nullable=False, default=arrow.utcnow) display_spec = db.Column(sau.ChoiceType(choices=[(k, v['name']) for k, v in DISPLAY_SPEC.items()]), nullable=False) color_spec = db.Column(sau.ChoiceType(choices=[(k, v['name']) for k, v in COLOR_SPEC.items()]), nullable=False) + image_format = db.Column(sau.ChoiceType(choices=[('BMP', 'BMP'), ('JPEG', 'JPEG'), ('PNG', 'PNG')]), nullable=False, default='BMP', server_default='BMP') + image_bit_depth = db.Column(db.Integer()) width = db.Column(db.Integer(), nullable=False, default=0) height = db.Column(db.Integer(), nullable=False, default=0) playlist_id = db.Column(db.BigInteger().with_variant(db.Integer, "sqlite"), db.ForeignKey(Playlist.id, onupdate='CASCADE', ondelete='SET NULL', name='fk_display_playlist')) @@ -365,6 +367,9 @@ def sync(cls): 'name': key, 'status': 'pending' if current_app.config['ENABLE_DISPLAY_APPROVAL'] else 'active', 'approval_code': cls.generate_approval_code(), + # Set image details on create only; this allows for editing later + 'image_format': request.args.get('i', 'BMP'), + 'image_bit_depth': request.args.get('ib'), }) display = cls.query.filter(cls.key == key).first() if display: diff --git a/app/templates/display/list.html.j2 b/app/templates/display/list.html.j2 index 1b77822..04cd0be 100644 --- a/app/templates/display/list.html.j2 +++ b/app/templates/display/list.html.j2 @@ -18,6 +18,7 @@