2

So as the title says, i'm trying to load a base64 encoded png image into a p5js image, here's a simplification of how im doing it:

PS: I'm using a base64 image because it's generated from a server

var img;
function setup() {
  // Canvas setup code ....

  img = loadImage('loading.png'); // show an image which says that it's loading
  img.loadPixels();

  // More setup code ...
}


// ...

function draw() {
  image(img, 0, 0, img.width, img.height);
  // ... more code
}

// ...

function receivedCallback(data) { // This function gets called once we receive the data
  // Data looks like this: { width: 100, height: 100, data: "...base64 data...." }
  img.resize(data.width, data.height);
  var imgData = data.data;
  var imgBlob = new Blob([imgData], { type: "image/png" }); // Create a blob
  var urlCreator = window.URL || window.webkitURL;
  var imageUrl = urlCreator.createObjectURL(imgBlob); // Craft the url
  img.src = imageUrl;
  img.loadPixels();
  img.updatePixels();
}

But, it's not working which is why I am asking here. If there is any way to do it I would appreciate it very much. Thanks in advance.

EDIT Steve's didn't quite work, I had to replace img.src = 'data:image/png;base64,' + data.data with img = loadImage('data:image/png;base64,' + data.data);

kenan238
  • 398
  • 4
  • 13

3 Answers3

1

You can use Data URLs directly. It can look something like this:

function receivedCallback(data) { // This function gets called once we receive the data
  // Data looks like this: { width: 100, height: 100, data: "...base64 data...." }
  loadImage('data:image/png;base64,' + data.data, function (newImage) {
    img = newImage;
  });
}

If you need to use blobs for some reason, you can look into base64 to blob conversions, like this answer.

Steve
  • 10,435
  • 15
  • 21
0

Steve's didn't quite work, I had to load the image instead of directly changing it's source. So instead of img.src = 'data:image/png;base64,' + data.data.

I needed to do img = loadImage('data:image/png;base64,' + data.data);

Thanks to Steve for his answer though!

kenan238
  • 398
  • 4
  • 13
0

There's no difference between loading a base64 image and a regular image from a file in P5.js. Simply replace the URL in your loadImage call with the base64 data.

In the interests of a runnable, complete example (note that the base64 image is very small):

const imgData = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII";
let img;

function preload() {
  img = loadImage(imgData);
}

function draw() {
  image(img, 0, 0);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.min.js"></script>

If your data is supposed to load at some indeterminate point in the future, say, after an interaction, then you can use a callback to determine when the image has loaded. This is somewhat use-case specific, but the pattern might look like:

const imgData = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII";
let img;

function setup() {
  createCanvas(300, 300);
}

function draw() {
  clear();
  textSize(14);
  text("click to load image", 10, 10);

  if (img) {
    image(img, 0, 0);
  }
}

function mousePressed() {
  if (!img) {
    loadImage(imgData, imgResult => {
      img = imgResult;
    });
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.min.js"></script>

If you have some async code in there, no problem. Just call loadImage whenever you have the base64 string ready, for example:

function mousePressed() {
  if (!img) {
    fetch("your endpoint")
      .then(response => response.json())
      .then(data => {  
        // prepend "data:image/png;base64," if
        // `data.data` doesn't already have it
        loadImage(data.data, imgResult => {
          img = imgResult;
        });
      });
  }
}

All this said, loading an image in response to a click is a somewhat odd pattern for most apps. Usually, you'd want to preload all of your asssets in the preload function so that you can render them immediately when requested. If you load via a fetch call in response to an event, there'll likely be an ugly-looking delay (or needing to show a temporary message/placeholder image/spinner) before rendering the image, so make sure this isn't an xy problem.

ggorlen
  • 44,755
  • 7
  • 76
  • 106