Skip to content
Advertisement

I’ll upload a WebGL rendered Canvas with toDataURL and imgurAPI, but it will be an image that will be a black image

I would appreciate any and all wisdom you can give me. Thank you in advance.

Assumptions

I wrote a Javascript game using a game engine called Phaser.js.

Issue.

  • [ ] When accessing the URL in the response, an image that is only black is displayed.

Response content from imgurAPI

{"id":"dwdGkep","title":null,"description":null,"datetime":1672621440,"type":"image/png","animated":false,"width":800,"height":800,"size":12914,"views":0,"bandwidth":0,"vote":null,"favorite":false,"nsfw":null,"section":null,"account_url":null,"account_id":0,"is_ad":false,"in_most_viral":false,"has_sound":false,"tags":[],"ad_type":0,"ad_url":"","edited":"0","in_gallery":false,"deletehash":"445HisfrUgBgogT","name":"","link":"https://i.imgur.com/dwdGkep.png"},"success":true,"status":200}

Since “success”:true, “status”:200, the upload process itself seems to be OK.

Relevant source code

Image sending process

function dataURLtoBlob(dataURL) {
  const bin = atob(dataURL.split(',')[1]);
  const buffer = new Uint8Array(bin.length);
  for (let i = 0; i < bin.length; i++) {
    buffer[i] = bin.charCodeAt(i);
  }
  return new Blob([buffer], { type: 'image/png' });
}

function canvasToImage() {
  //get image data from canvas element
  //const canvas = document.getElementsByTagName("canvas")[0];
  var gl = game.canvas.getContext("webgl", { preserveDrawingBuffer: true });
  const imageData = game.canvas.toDataURL("image/png");

  // convert image data to Blob format
  const blob = dataURLtoBlob(imageData);

  const xhr = new XMLHttpRequest();

  xhr.open("POST", "https://api.imgur.com/3/image");
  xhr.setRequestHeader('Authorization', 'Client-ID XXXXXXXXXXXXXXXX');

  xhr.onload = function() {
    if (xhr.status === 200) {
      console.log(xhr.response);
    } else {
      console.error(xhr.response.data); }
    }
  };

  xhr.send(blob);
}

The calling part of the above process (excerpt) Like setting up an event in the create function.

tweetButton.on("pointerdown", () => {
// Put what happens when the button is clicked here
canvasToImage();
});

Here is the entire code create function

var tweetButton;

// create a game object
function create() {
    // display the tweetButton
    tweetButton = this.add.image(400, 500, "tweetButton");
    tweetButton.setDepth(10);
    tweetButton.setVisible(false); tweetButton;
    tweetButton.setInteractive();
    tweetButton.on("pointerdown", () => {
    // Write the process to be executed when the button is clicked here
    canvasToImage();
    });
    var startScreenImage = this.add.image(400, 400, "startScreen");
    startScreenImage.setInteractive();
    // Create text to display the timer
    this.gameTitle = this.add.text(200, 155, "Endless Chase", {
    fontSize: "70px",
    fill: "#000000" // make the text color black
    });
    this.startMessage = this.add.text(200, 250, "Click to start game", {
    fontSize: "40px",
    fill: "#000000" // set text color to black
    });
    this.Notion = this.add.text(200, 600, "*The game is only for play on a computer", {
    fontSize: "30px",
    fill: "#DC143C" // set text color to black
    });
    startScreenImage.on("pointerdown", () => {
    // delete startScreenImage when clicked
    this.gameTitle.destroy();
    this.startMessage.destroy();
    this.Notion.destroy(); this.startMessage.destroy(); this.startScreenImage.destroy()
    startScreenImage.destroy();
    startScreenImage_flg = true;
    // Write the process to start the game here
    // Create a cursor
    this.cursor = this.add.circle(400, 400, 17, 0x0000ff);
    // create red circles
    this.redCircles = [];
    var redCircle = this.add.circle(
        Phaser.Math.Between(
        this.cursor.x - clearance, // coordinates 300px left from cursor
        this.cursor.x + clearance // coordinates 300px right of cursor
        ),
        Phaser.Math.Between(
        this.cursor.y - clearance, // coordinates 300px above the cursor
        this.cursor.y + clearance // coordinates 300px below cursor
        ),
        50, // radius
        0xff0000 // color
    );
    this.redCircles.push(redCircle);
    // start the process of adding a red circle every 5 seconds
    this.intervalId = setInterval(() => {
        // create a red circle
        var redCircle = this.add.circle(
        Phaser.Math.Between(this.cursor.x - clearance, this.cursor.x + clearance),
        Phaser.Math.Between(this.cursor.y - clearance, this.cursor.y + clearance),
        50, // radius
        0xff0000 // color
        );
        redCircle.speed = Math.random() * 0.09 + 0.01;
        this.redCircles.push(redCircle);
    }, 5000);

    // Create text to display the timer
    this.timerText = this.add.text(10, 10, "0 seconds", {
        fontSize: "32px",
        fill: "#000000" // make the text color black
    });
    // initialize the timer
    this.elapsedTime = 0;
    this.prevTime = this.time.now;
    this.gameOverText = this.add.text(200, 300, "", {
        fontSize: "32px",
        fill: "#000000" // set text color to black
    });
    this.gameOverText.setDepth(10);
    });
    // Register a process to end the game when the tab becomes inactive
    document.addEventListener("visibilitychange", () => {
    this.gameOver = true;
    startScreenImage_flg = true;
    clearInterval(this.intervalId);
    });
}

What I tried

I tried the following for the same event, but it didn’t work.

var gl = game.canvas.getContext("webgl", { preserveDrawingBuffer: true });

Current Processing

  • [ ] Clicking an element in the Canvas on the Result screen will perform the following series of processes.
  • [ ] Convert the WebGL canvas to an image using toDataURL().
  • [ ] Convert the image data to Blob format.
  • [ ] Upload the data in Blob format with XMLHttpRequest using imgur’s API
  • [ ] Obtain the URL in the response`

Advertisement

Answer

The easy solution is to use phaser’s builtin function snapshot (link to the documentation), the resulting image should display in the correct fashion (no special configuration needed).

This function creates an snapshot-image of the current canvas and passes the taken image as a HTMLImageElement to a given callback function.

You would just have to change your code to look something like this:

function canvasToImage() {

    game.renderer.snapshot(function (image) {
        
        const blob = dataURLtoBlob(image.src);
        const xhr = new XMLHttpRequest();

        ...

        xhr.send(blob);         
    });

}

(I tested it, on one of my local projects, and it works)

If this solution doesn’t work for you, you could open an issue on the phaser github or mention it on the discourse page, and fix might be made for the next version.

User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement