typemytype / drawbot

http://www.drawbot.com
Other
398 stars 61 forks source link

ImageObject renders scaled when its image input dpi metadata is not 72 #508

Open typoman opened 1 year ago

typoman commented 1 year ago

I found out that if I load an image using the ImageObject it renders small and blurred when the image metadata dpi is 300. If I change the dpi of the image to 72 outside drawbot (without changing the number of pixels) drawbot renders it as the actual image. I found a solution by loading the image using TIFFRepresentation instead of the current method in the _makeBitmapImageRep function. This method always keeps the right size regardless of the input image dpi metadata. This code sample only overrides the function _makeBitmapImageRep to show the difference. I'm not sure what would be the other effects of this change:

import AppKit
from drawBot.misc import DrawBotError, optimizePath
import Quartz
import os
from drawBot import *
from drawBot.context.tools.imageObject import ImageObject

def _makeBitmapImageRep(nsImage=None, pdfPage=None, imageResolution=72.0, antiAliasing=False, colorSpaceName=AppKit.NSCalibratedRGBColorSpace):
    """Construct a bitmap image representation at a given resolution."""
    if nsImage is None and pdfPage is None:
        raise DrawBotError("At least a image or a pdf page must be provided to create a bitmap representaion.")

    if pdfPage is not None:
        mediaBox = pdfPage.boundsForBox_(Quartz.kPDFDisplayBoxMediaBox)
        width, height = mediaBox.size
    elif nsImage is not None:
        width, height = nsImage.size()
    rep = AppKit.NSBitmapImageRep.imageRepWithData_(nsImage.TIFFRepresentation())
    return rep

def _open(self, path):
    """
    Open an image with a given `path`.
    """
    if isinstance(path, AppKit.NSImage):
        im = path
    elif isinstance(path, str):
        path = optimizePath(path)
        if path.startswith("http"):
            url = AppKit.NSURL.URLWithString_(path)
        else:
            if not os.path.exists(path):
                raise DrawBotError("Image path '%s' does not exists." % path)
            url = AppKit.NSURL.fileURLWithPath_(path)
        im = AppKit.NSImage.alloc().initByReferencingURL_(url)
    else:
        raise DrawBotError("Cannot read image path '%s'." % path)
    rep = _makeBitmapImageRep(im)
    ciImage = AppKit.CIImage.alloc().initWithBitmapImageRep_(rep)
    self._merge(ciImage, doCrop=True)

# remove the following line to see the default behavior
ImageObject.open = _open
imagePath = "sampleImage-300dpi.jpg"
i = ImageObject()
i.open(imagePath)
w, h = imageSize(i)
size(w, h)
typemytype commented 1 year ago

it used to be the TIFFRrepesentation but that changed here: https://github.com/typemytype/drawbot/commit/137a75e3967f78551e94146fe1be529ae1b260ba

with the commit message:

  • all image rep must be build with _makeBitmapImageRep when the imageObject reads a path, when the imageObjects applies a filter

Its not perfect now, I guess the image resolution needs to be checked on open and used while drawing into the imageObject.

Optionally the imageObject could have an imageResolution argument during init when there is no path/image given: ImageObject(imageResolution=144).