mirror of
https://github.com/rbreu/beeref.git
synced 2026-03-11 08:54:28 +00:00
Antialiasing/smoothing for displaying images
This commit is contained in:
parent
8a9cab9a73
commit
608787d9a9
7 changed files with 103 additions and 51 deletions
|
|
@ -4,10 +4,15 @@
|
|||
Added
|
||||
-----
|
||||
|
||||
* Embedded images can now be JPG or PNG. By default, small images and
|
||||
images with an alpha channel will be stored as PNG, the rest as
|
||||
JPG. In the newly created settings dialog, this behaviour can be
|
||||
changed to always use PNG (the former behaviour) always JPG.
|
||||
* Images can now be stored JPG or PNG inside the bee file. By default,
|
||||
small images and images with an alpha channel will be stored as PNG,
|
||||
the rest as JPG. In the newly created settings dialog, this
|
||||
behaviour can be changed to always use PNG (the former behaviour) or
|
||||
always JPG.
|
||||
* Antialias/smoothing for displaying images. For images being
|
||||
displayed at a large zoom factor, smoothing will turn off to make
|
||||
sure that icons, pixel sprites etc can be viewed correctly.
|
||||
|
||||
|
||||
Fixed
|
||||
-----
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ settings_events = BeeSettingsEvents()
|
|||
class BeeSettings(QtCore.QSettings):
|
||||
|
||||
DEFAULTS = {
|
||||
'FileIO/image_storage_format': 'best',
|
||||
'Items/image_storage_format': 'best',
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
|
|
|
|||
|
|
@ -134,13 +134,14 @@ class BeePixmapItem(BeeItemMixin, QtWidgets.QGraphicsPixmapItem):
|
|||
def get_imgformat(self, img):
|
||||
"""Determines the format for storing this image."""
|
||||
|
||||
formt = self.settings.valueOrDefault('FileIO/image_storage_format')
|
||||
formt = self.settings.valueOrDefault('Items/image_storage_format')
|
||||
if formt not in ('png', 'jpg', 'best'):
|
||||
formt = 'best'
|
||||
|
||||
if formt == 'best':
|
||||
# Images with alpha channel and small images are stored as png
|
||||
if (img.hasAlphaChannel()
|
||||
or (img.height() < 200 and img.width() < 200)):
|
||||
or (img.height() < 300 and img.width() < 300)):
|
||||
formt = 'png'
|
||||
else:
|
||||
formt = 'jpg'
|
||||
|
|
@ -300,6 +301,12 @@ class BeePixmapItem(BeeItemMixin, QtWidgets.QGraphicsPixmapItem):
|
|||
painter.drawRect(rect)
|
||||
|
||||
def paint(self, painter, option, widget):
|
||||
if painter.combinedTransform().m11() < 3:
|
||||
# We want image smoothing, but only for images where we
|
||||
# are not zoomed in a lot. This is to ensure that for
|
||||
# example icons and pixel sprites can be viewed correctly.
|
||||
painter.setRenderHint(painter.RenderHint.SmoothPixmapTransform)
|
||||
|
||||
if self.crop_mode:
|
||||
self.paint_debug(painter, option, widget)
|
||||
|
||||
|
|
|
|||
|
|
@ -25,28 +25,23 @@ from beeref.config import BeeSettings, settings_events
|
|||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ImageStorageFormatWidget(QtWidgets.QGroupBox):
|
||||
KEY = 'FileIO/image_storage_format'
|
||||
OPTIONS = (
|
||||
('best', 'Best Guess',
|
||||
('Small images and images with alpha channel are stored as png,'
|
||||
' everything else as jpg')),
|
||||
('png', 'Always PNG', 'Lossless, but large bee file'),
|
||||
('jpg', 'Always JPG',
|
||||
'Small bee file, but lossy and no transparency support'))
|
||||
class RadioGroup(QtWidgets.QGroupBox):
|
||||
TITLE = None
|
||||
HELPTEXT = None
|
||||
KEY = None
|
||||
OPTIONS = None
|
||||
|
||||
def __init__(self, parent):
|
||||
super().__init__('Image Storage Format:')
|
||||
parent.settings_widgets.append(self)
|
||||
def __init__(self):
|
||||
super().__init__(self.TITLE)
|
||||
self.settings = BeeSettings()
|
||||
settings_events.restore_defaults.connect(self.on_restore_defaults)
|
||||
layout = QtWidgets.QVBoxLayout()
|
||||
self.setLayout(layout)
|
||||
helptxt = QtWidgets.QLabel(
|
||||
'How images are stored inside bee files.'
|
||||
' Changes will only take effect on newly saved images.')
|
||||
helptxt.setWordWrap(True)
|
||||
layout.addWidget(helptxt)
|
||||
settings_events.restore_defaults.connect(self.on_restore_defaults)
|
||||
|
||||
if self.HELPTEXT:
|
||||
helptxt = QtWidgets.QLabel(self.HELPTEXT)
|
||||
helptxt.setWordWrap(True)
|
||||
layout.addWidget(helptxt)
|
||||
|
||||
self.ignore_values_changed = True
|
||||
self.buttons = {}
|
||||
|
|
@ -78,19 +73,31 @@ class ImageStorageFormatWidget(QtWidgets.QGroupBox):
|
|||
self.ignore_values_changed = False
|
||||
|
||||
|
||||
class ImageStorageFormatWidget(RadioGroup):
|
||||
TITLE = 'Image Storage Format:'
|
||||
HELPTEXT = ('How images are stored inside bee files.'
|
||||
' Changes will only take effect on newly saved images.')
|
||||
KEY = 'Items/image_storage_format'
|
||||
OPTIONS = (
|
||||
('best', 'Best Guess',
|
||||
('Small images and images with alpha channel are stored as png,'
|
||||
' everything else as jpg')),
|
||||
('png', 'Always PNG', 'Lossless, but large bee file'),
|
||||
('jpg', 'Always JPG',
|
||||
'Small bee file, but lossy and no transparency support'))
|
||||
|
||||
|
||||
class SettingsDialog(QtWidgets.QDialog):
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle(f'{constants.APPNAME} Settings')
|
||||
tabs = QtWidgets.QTabWidget()
|
||||
|
||||
self.settings_widgets = []
|
||||
|
||||
# Miscellaneous
|
||||
misc = QtWidgets.QWidget()
|
||||
misc_layout = QtWidgets.QGridLayout()
|
||||
misc.setLayout(misc_layout)
|
||||
misc_layout.addWidget(ImageStorageFormatWidget(self), 0, 0)
|
||||
misc_layout.addWidget(ImageStorageFormatWidget(), 0, 0)
|
||||
tabs.addTab(misc, '&Miscellaneous')
|
||||
|
||||
layout = QtWidgets.QVBoxLayout()
|
||||
|
|
|
|||
|
|
@ -83,21 +83,21 @@ def test_get_extra_save_data(item):
|
|||
|
||||
def test_get_imgformat_test_with_real_image(
|
||||
qapp, imgfilename3x3, item, settings):
|
||||
settings.setValue('FileIO/image_storage_format', 'best')
|
||||
settings.setValue('Items/image_storage_format', 'best')
|
||||
img = QtGui.QImage(imgfilename3x3)
|
||||
assert item.get_imgformat(img) == 'png'
|
||||
|
||||
|
||||
def test_get_imgformat_unknown_option_defaults_to_best(
|
||||
qapp, imgfilename3x3, item, settings):
|
||||
settings.setValue('FileIO/image_storage_format', 'foo')
|
||||
settings.setValue('Items/image_storage_format', 'foo')
|
||||
img = QtGui.QImage(imgfilename3x3)
|
||||
assert item.get_imgformat(img) == 'png'
|
||||
|
||||
|
||||
def test_get_imgformat_jpg_for_large_nonalpha_image_when_setting_best(
|
||||
qapp, settings, item):
|
||||
settings.setValue('FileIO/image_storage_format', 'best')
|
||||
settings.setValue('Items/image_storage_format', 'best')
|
||||
img = MagicMock(
|
||||
hasAlphaChannel=MagicMock(return_value=False),
|
||||
height=MagicMock(return_value=1600),
|
||||
|
|
@ -107,7 +107,7 @@ def test_get_imgformat_jpg_for_large_nonalpha_image_when_setting_best(
|
|||
|
||||
def test_get_imgformat_png_for_large_alpha_image_when_setting_best(
|
||||
qapp, settings, item):
|
||||
settings.setValue('FileIO/image_storage_format', 'best')
|
||||
settings.setValue('Items/image_storage_format', 'best')
|
||||
img = MagicMock(
|
||||
hasAlphaChannel=MagicMock(return_value=True),
|
||||
height=MagicMock(return_value=1600),
|
||||
|
|
@ -117,7 +117,7 @@ def test_get_imgformat_png_for_large_alpha_image_when_setting_best(
|
|||
|
||||
def test_get_imgformat_png_for_small_nonalpha_image_when_setting_best(
|
||||
qapp, settings, item):
|
||||
settings.setValue('FileIO/image_storage_format', 'best')
|
||||
settings.setValue('Items/image_storage_format', 'best')
|
||||
img = MagicMock(
|
||||
hasAlphaChannel=MagicMock(return_value=False),
|
||||
height=MagicMock(return_value=100),
|
||||
|
|
@ -127,7 +127,7 @@ def test_get_imgformat_png_for_small_nonalpha_image_when_setting_best(
|
|||
|
||||
def test_get_imgformat_jpg_when_setting_jpg(
|
||||
qapp, settings, item):
|
||||
settings.setValue('FileIO/image_storage_format', 'jpg')
|
||||
settings.setValue('Items/image_storage_format', 'jpg')
|
||||
img = MagicMock(
|
||||
hasAlphaChannel=MagicMock(return_value=True),
|
||||
height=MagicMock(return_value=100),
|
||||
|
|
@ -137,7 +137,7 @@ def test_get_imgformat_jpg_when_setting_jpg(
|
|||
|
||||
def test_get_imgformat_png_when_setting_png(
|
||||
qapp, settings, item):
|
||||
settings.setValue('FileIO/image_storage_format', 'png')
|
||||
settings.setValue('Items/image_storage_format', 'png')
|
||||
img = MagicMock(
|
||||
hasAlphaChannel=MagicMock(return_value=False),
|
||||
height=MagicMock(return_value=1600),
|
||||
|
|
@ -153,7 +153,7 @@ def test_pixmap_to_bytes_png(qapp, imgfilename3x3):
|
|||
|
||||
|
||||
def test_pixmap_to_bytes_jpg(qapp, imgfilename3x3, settings):
|
||||
settings.setValue('FileIO/image_storage_format', 'jpg')
|
||||
settings.setValue('Items/image_storage_format', 'jpg')
|
||||
item = BeePixmapItem(QtGui.QImage(imgfilename3x3))
|
||||
data, imgformat = item.pixmap_to_bytes()
|
||||
assert imgformat == 'jpg'
|
||||
|
|
@ -338,7 +338,10 @@ def test_paint(qapp, item):
|
|||
item.pixmap = MagicMock()
|
||||
item.paint_selectable = MagicMock()
|
||||
item.crop = QtCore.QRectF(10, 20, 30, 40)
|
||||
painter = MagicMock()
|
||||
painter = MagicMock(
|
||||
combinedTransform=MagicMock(
|
||||
return_value=MagicMock(
|
||||
m11=MagicMock(return_value=0.5))))
|
||||
item.paint(painter, None, None)
|
||||
item.paint_selectable.assert_called_once()
|
||||
painter.drawPixmap.assert_called_with(
|
||||
|
|
@ -353,7 +356,10 @@ def test_paint_when_crop_mode(qapp, item):
|
|||
item.crop = QtCore.QRectF(10, 20, 30, 40)
|
||||
item.crop_mode = True
|
||||
item.crop_temp = QtCore.QRectF(11, 22, 29, 39)
|
||||
painter = MagicMock()
|
||||
painter = MagicMock(
|
||||
combinedTransform=MagicMock(
|
||||
return_value=MagicMock(
|
||||
m11=MagicMock(return_value=0.5))))
|
||||
item.paint(painter, None, None)
|
||||
item.paint_selectable.assert_not_called()
|
||||
painter.drawPixmap.assert_called_with(0, 0, item.pixmap())
|
||||
|
|
|
|||
|
|
@ -86,7 +86,10 @@ def test_select_handle_free_center(view, item):
|
|||
|
||||
def test_draw_debug_shape_rect(view, item):
|
||||
view.scene.addItem(item)
|
||||
painter = MagicMock()
|
||||
painter = MagicMock(
|
||||
combinedTransform=MagicMock(
|
||||
return_value=MagicMock(
|
||||
m11=MagicMock(return_value=0.5))))
|
||||
item.draw_debug_shape(
|
||||
painter,
|
||||
QtCore.QRectF(5, 6, 20, 30),
|
||||
|
|
@ -97,7 +100,10 @@ def test_draw_debug_shape_rect(view, item):
|
|||
|
||||
def test_draw_debug_shape_path(view, item):
|
||||
view.scene.addItem(item)
|
||||
painter = MagicMock()
|
||||
painter = MagicMock(
|
||||
combinedTransform=MagicMock(
|
||||
return_value=MagicMock(
|
||||
m11=MagicMock(return_value=0.5))))
|
||||
path = QtGui.QPainterPath()
|
||||
path.addRect(QtCore.QRectF(5, 6, 20, 30))
|
||||
item.draw_debug_shape(
|
||||
|
|
@ -111,7 +117,10 @@ def test_draw_debug_shape_path(view, item):
|
|||
@patch('beeref.items.BeePixmapItem.draw_debug_shape')
|
||||
def test_paint_when_not_selected(debug_mock, view, item):
|
||||
view.scene.addItem(item)
|
||||
painter = MagicMock()
|
||||
painter = MagicMock(
|
||||
combinedTransform=MagicMock(
|
||||
return_value=MagicMock(
|
||||
m11=MagicMock(return_value=0.5))))
|
||||
item.setSelected(False)
|
||||
item.paint(painter, None, None)
|
||||
painter.drawPixmap.assert_called_once()
|
||||
|
|
@ -122,7 +131,10 @@ def test_paint_when_not_selected(debug_mock, view, item):
|
|||
|
||||
def test_paint_when_selected_single_selection(view, item):
|
||||
view.scene.addItem(item)
|
||||
painter = MagicMock()
|
||||
painter = MagicMock(
|
||||
combinedTransform=MagicMock(
|
||||
return_value=MagicMock(
|
||||
m11=MagicMock(return_value=0.5))))
|
||||
item.setSelected(True)
|
||||
item.paint(painter, None, None)
|
||||
painter.drawPixmap.assert_called_once()
|
||||
|
|
@ -135,7 +147,10 @@ def test_paint_when_selected_multi_selection(view, item):
|
|||
item2 = BeePixmapItem(QtGui.QImage())
|
||||
item2.setSelected(True)
|
||||
view.scene.addItem(item2)
|
||||
painter = MagicMock()
|
||||
painter = MagicMock(
|
||||
combinedTransform=MagicMock(
|
||||
return_value=MagicMock(
|
||||
m11=MagicMock(return_value=0.5))))
|
||||
item.setSelected(True)
|
||||
item.paint(painter, None, None)
|
||||
painter.drawPixmap.assert_called_once()
|
||||
|
|
@ -150,7 +165,11 @@ def test_paint_when_debug_shapes(view):
|
|||
args_mock.debug_boundingrects = False
|
||||
args_mock.debug_handles = False
|
||||
item = BeePixmapItem(QtGui.QImage())
|
||||
item.paint(MagicMock(), None, None)
|
||||
painter = MagicMock(
|
||||
combinedTransform=MagicMock(
|
||||
return_value=MagicMock(
|
||||
m11=MagicMock(return_value=0.5))))
|
||||
item.paint(painter, None, None)
|
||||
m.assert_called_once()
|
||||
|
||||
|
||||
|
|
@ -161,7 +180,11 @@ def test_paint_when_debug_boundingrects(view):
|
|||
args_mock.debug_boundingrects = True
|
||||
args_mock.debug_handles = False
|
||||
item = BeePixmapItem(QtGui.QImage())
|
||||
item.paint(MagicMock(), None, None)
|
||||
painter = MagicMock(
|
||||
combinedTransform=MagicMock(
|
||||
return_value=MagicMock(
|
||||
m11=MagicMock(return_value=0.5))))
|
||||
item.paint(painter, None, None)
|
||||
m.assert_called_once()
|
||||
|
||||
|
||||
|
|
@ -174,7 +197,11 @@ def test_paint_when_debug_handles(view):
|
|||
item = BeePixmapItem(QtGui.QImage())
|
||||
view.scene.addItem(item)
|
||||
item.setSelected(True)
|
||||
item.paint(MagicMock(), None, None)
|
||||
painter = MagicMock(
|
||||
combinedTransform=MagicMock(
|
||||
return_value=MagicMock(
|
||||
m11=MagicMock(return_value=0.5))))
|
||||
item.paint(painter, None, None)
|
||||
m.assert_called()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -36,18 +36,18 @@ def test_command_line_args_get_unknown():
|
|||
|
||||
|
||||
def test_settings_value_or_default_gets_default(settings):
|
||||
assert settings.valueOrDefault('FileIO/image_storage_format') == 'best'
|
||||
assert settings.valueOrDefault('Items/image_storage_format') == 'best'
|
||||
|
||||
|
||||
def test_settings_value_or_default_gets_overriden_value(settings):
|
||||
settings.setValue('FileIO/image_storage_format', 'png')
|
||||
assert settings.valueOrDefault('FileIO/image_storage_format') == 'png'
|
||||
settings.setValue('Items/image_storage_format', 'png')
|
||||
assert settings.valueOrDefault('Items/image_storage_format') == 'png'
|
||||
|
||||
|
||||
def test_restore_defaults_restores(settings):
|
||||
settings.setValue('FileIO/image_storage_format', 'png')
|
||||
settings.setValue('Items/image_storage_format', 'png')
|
||||
settings.restore_defaults()
|
||||
assert settings.contains('FileIO/image_storage_format') is False
|
||||
assert settings.contains('Items/image_storage_format') is False
|
||||
|
||||
|
||||
def test_restore_defaults_leaves_other_settings(settings):
|
||||
|
|
|
|||
Loading…
Reference in a new issue