Hello @moturi.magati.george and thanks for you reply.
As you can see in my screenshot i resized the icon to 32x32 (96dpi) but this leads to a quality that is not usable and far from the quality of native revit icons and also far from the quality of pyrevit icons.
So there must be some tricks how to scale an icon with better quality.
pyRevit takes images with a maximum of 96x96 pixels ant then scales the programmatically and even sets the dpi corresponding to the display dpi.
If no one has a tip for me how I can better scale down my icon I will have to rebuild the method pyrevit uses:
ICON_SMALL = 16
ICON_MEDIUM = 24
ICON_LARGE = 32
DEFAULT_DPI = 96
DEFAULT_TOOLTIP_IMAGE_FORMAT = '.png'
DEFAULT_TOOLTIP_VIDEO_FORMAT = '.swf'
if not EXEC_PARAMS.doc_mode and HOST_APP.is_newer_than(2019, or_equal=True):
DEFAULT_TOOLTIP_VIDEO_FORMAT = '.mp4'
def argb_to_brush(argb_color):
a = r = g = b = "FF"
try:
b = argb_color[-2:]
g = argb_color[-4:-2]
r = argb_color[-6:-4]
if len(argb_color) > 7:
a = argb_color[-8:-6]
return Media.SolidColorBrush(Media.Color.FromArgb(
Convert.ToInt32("0x" + a, 16),
Convert.ToInt32("0x" + r, 16),
Convert.ToInt32("0x" + g, 16),
Convert.ToInt32("0x" + b, 16)
)
)
except Exception as color_ex:
mlogger.error("Bad color format %s | %s", argb_color, color_ex)
def load_bitmapimage(image_file):
"""Load given png file.
Args:
image_file (str): image file path
Returns:
(Imaging.BitmapImage): bitmap image object
"""
bitmap = Imaging.BitmapImage()
bitmap.BeginInit()
bitmap.UriSource = Uri(image_file)
bitmap.CacheOption = Imaging.BitmapCacheOption.OnLoad
bitmap.CreateOptions = Imaging.BitmapCreateOptions.IgnoreImageCache
bitmap.EndInit()
bitmap.Freeze()
return bitmap
class PyRevitUIError(PyRevitException):
"""Common base class for all pyRevit ui-related exceptions."""
pass
class ButtonIcons(object):
"""pyRevit ui element icon.
Upon init, this type reads the given image file into an io stream and
releases the os lock on the file.
Args:
image_file (str): image file path to be used as icon
Attributes:
icon_file_path (str): icon image file path
filestream (IO.FileStream): io stream containing image binary data
"""
def __init__(self, image_file):
self.icon_file_path = image_file
self.check_icon_size()
self.filestream = IO.FileStream(image_file,
IO.FileMode.Open,
IO.FileAccess.Read)
@staticmethod
def recolour(image_data, size, stride, color):
step = stride / size
for i in range(0, stride, step):
for j in range(0, stride, step):
idx = (i * size) + j
image_data[idx] = color >> 0 & 0xff
image_data[idx+1] = color >> 8 & 0xff
image_data[idx+2] = color >> 16 & 0xff
def check_icon_size(self):
"""Verify icon size is within acceptable range."""
image = System.Drawing.Image.FromFile(self.icon_file_path)
image_size = max(image.Width, image.Height)
if image_size > 96:
mlogger.warning('Icon file is too large. Large icons adversely '
'affect the load time since they need to be '
'processed and adjusted for screen scaling. '
'Keep icons at max 96x96 pixels: %s',
self.icon_file_path)
def create_bitmap(self, icon_size):
"""Resamples image and creates bitmap for the given size.
Icons are assumed to be square.
Args:
icon_size (int): icon size (width or height)
Returns:
(Imaging.BitmapSource): object containing image data at given size
"""
mlogger.debug('Creating %sx%s bitmap from: %s',
icon_size, icon_size, self.icon_file_path)
adjusted_icon_size = icon_size * 2
adjusted_dpi = DEFAULT_DPI * 2
screen_scaling = HOST_APP.proc_screen_scalefactor
self.filestream.Seek(0, IO.SeekOrigin.Begin)
base_image = Imaging.BitmapImage()
base_image.BeginInit()
base_image.StreamSource = self.filestream
base_image.DecodePixelHeight = int(adjusted_icon_size * screen_scaling)
base_image.EndInit()
self.filestream.Seek(0, IO.SeekOrigin.Begin)
image_size = base_image.PixelWidth
image_format = base_image.Format
image_byte_per_pixel = int(base_image.Format.BitsPerPixel / 8)
palette = base_image.Palette
stride = int(image_size * image_byte_per_pixel)
array_size = stride * image_size
image_data = System.Array.CreateInstance(System.Byte, array_size)
base_image.CopyPixels(image_data, stride, 0)
scaled_size = int(adjusted_icon_size * screen_scaling)
scaled_dpi = int(adjusted_dpi * screen_scaling)
bitmap_source = \
Imaging.BitmapSource.Create(scaled_size, scaled_size,
scaled_dpi, scaled_dpi,
image_format,
palette,
image_data,
stride)
return bitmap_source
@property
def small_bitmap(self):
"""Resamples image and creates bitmap for size :obj:`ICON_SMALL`.
Returns:
(Imaging.BitmapSource): object containing image data at given size
"""
return self.create_bitmap(ICON_SMALL)
@property
def medium_bitmap(self):
"""Resamples image and creates bitmap for size :obj:`ICON_MEDIUM`.
Returns:
(Imaging.BitmapSource): object containing image data at given size
"""
return self.create_bitmap(ICON_MEDIUM)
@property
def large_bitmap(self):
"""Resamples image and creates bitmap for size :obj:`ICON_LARGE`.
Returns:
(Imaging.BitmapSource): object containing image data at given size
"""
return self.create_bitmap(ICON_LARGE)