Low quality png in widget

I made a widget that downloads an image based on date and crops into the appropriate part, the image is small - 217 KB

https://root.breslan.co.uk/scratch/LP/210118.png

When I run the script in the app I get a lovely sharp image in my QuickLook.
http://root.breslan.co.uk/scratch/E0ADD14B-F0F0-4C95-B01B-13A26479493E.png

However in the widget it is very blurry…

Any idea how to improve the quality in the widget?

full code is below:

var d = getPreviousMonday()
var df = new DateFormatter()
df.dateFormat = "yyMMdd"
dateString = df.string(d)

let url = "https://root.breslan.co.uk/scratch/LP/" + dateString + ".png"
let req = new Request(url)
let originalImage = await req.loadImage()

var td = new Date()
td = td.getDay()

positionRow = [1,1,1,2,2,2,2]
positionColumn = [1,2,3,1,1,2,2]

let w = originalImage.size.width/3
let h = originalImage.size.height/2

yp = -(positionRow[td-1]-1)*h
xp = -(positionColumn[td-1]-1)*w

console.log("x:" + xp + " and y:" + yp)

dc = new DrawContext()
dc.opaque = true
dc.size = new Size(w,h)

dc.drawImageAtPoint(originalImage, new Point(xp,yp))

image = dc.getImage()


if (config.runsInWidget) {
  // create and show widget
  let widget = new ListWidget()
  widget.backgroundImage = image
  widget.setPadding(0, 0, 0, 0)
  widget.url = url
  Script.setWidget(widget)
  Script.complete()
} else {  
  QuickLook.present(image)
}
  

function getPreviousMonday()
{
    var date = new Date();
    var day = date.getDay();
    var prevMonday = new Date();
    if(date.getDay() == 0){
        prevMonday.setDate(date.getDate() - 7);
    }
    else{
        prevMonday.setDate(date.getDate() - (day-1));
    }

    return prevMonday;
}

I think you need to set the respectScreenScale of your DrawContext to true. See https://docs.scriptable.app/drawcontext/#respectscreenscale

Thanks for the advise, but that did not work :frowning:

I’ve now tried it myself and after some debugging found the issue: Scriptable seems to scale the original image down to 500 px in the widget probably because of memory limits. Since these limits don’t exist in the app, the image is loaded in full size.

Thinking about it, it doesn’t make much sense since to scale the image down, it needs to load the full image first. But maybe the image is loaded in a different thread that has more memory available than the widget and to use it in the widget it needs to be scaled down. I don’t know the reason, only @simonbs knows it since he made Scriptable.

To solve the issue you could instead of download an image for the whole week and crop it in Scriptable, do that step beforehand when creating the image and then download an image for each day.
If this is not possible for you, you could download the image on monday with a different script (or the same script, but only when it is running in the app), crop it there for each day and save them to your iCloud Drive/local file storage of Scriptable and use these files then in the widget.

Thanks for the response. I might make a small php utility on my that does the cropping on the server side.

Maybe you can read the file manually, draw it using DrawContext, and then set it as the background image. This approach has worked for me; my code looks like this:

// Function to create a new image using DrawContext
function createImageFromFile(filePath) {
    let originalImage;
    const fileManager = FileManager.local();
    if (fileManager.fileExists(filePath)) {
        const file = fileManager.read(filePath);
				return createImageFromData(file);
		} else {
        console.error("image File does not exist.");
        return null;
    }
}


// Function to create a new image using DrawContext
function createImageFromData(imageData) {
// 		let imageData = Data.fromBase64String(base64String);
		if (imageData == null) {
            console.log("image data is null:" + base64String);
			return null;
		}
    let originalImage = Image.fromData(imageData);
    
    // Define the maximum length, to limit memory usage.
    // If it doesn't work, you can try setting it to 300. This may prevent the Scriptable compress image operation from being triggered.
    const maxLength = 500;

    // Calculate the scaling factor to maintain aspect ratio
    let scaleFactor = 1;
    if (originalImage.size.width > maxLength || originalImage.size.height > maxLength) {
        const widthScale = maxLength / originalImage.size.width;
        const heightScale = maxLength / originalImage.size.height;
        scaleFactor = Math.min(widthScale, heightScale);
    }

    // Calculate new dimensions
    const newWidth = originalImage.size.width * scaleFactor;
    const newHeight = originalImage.size.height * scaleFactor;

    // Create a DrawContext
    let ctx = new DrawContext();
    ctx.respectScreenScale = true;
    ctx.size = new Size(newWidth-1, newHeight);

    // Draw the original image onto the context with the new size
    ctx.drawImageInRect(originalImage, new Rect(0, 0, newWidth, newHeight));

    // Get the new image from the DrawContext
    return ctx.getImage();
}