2010-04-22 17 views
24

La mia domanda è, qual è il modo migliore per colorare un'immagine che viene disegnata usando il metodo drawImage. L'utilizzo del target per questo è avanzato effetti 2D (sviluppo del gioco) in cui le particelle cambiano colore nel tempo, ecc. Non sto chiedendo come colorare tutta la tela, solo l'immagine corrente che sto per disegnare.Come tintare un'immagine con HTML5 Canvas?

Ho concluso che il parametro globalAlpha influisce sull'immagine corrente disegnata.

//works with drawImage() 
canvas2d.globalAlpha = 0.5; 

Ma come si tinge ogni immagine con un valore di colore arbitrario? Sarebbe fantastico se ci fosse una sorta di globalFillStyle o globalColor o cose del genere ...

EDIT:

Ecco uno screenshot dell'applicazione sto lavorando con: http://twitpic.com/1j2aeg/full alt text http://web20.twitpic.com/img/92485672-1d59e2f85d099210d4dafb5211bf770f.4bd804ef-scaled.png

risposta

4

Quando ho creato un test delle particelle ho semplicemente memorizzato le immagini in cache in base alla rotazione (come 35 rotazioni), al colore e all'alfa e ho creato un wrapper in modo che fossero creati automaticamente. Ha funzionato bene Sì, dovrebbe esserci qualche tipo di operazione di tinta, ma quando si ha a che fare con il rendering del software, la cosa migliore da fare in Flash è memorizzare tutto. Particle Example I made for fun

<!DOCTYPE HTML> 
<html lang="en"> 
<head> 
<title>Particle Test</title> 
<script language="javascript" src="../Vector.js"></script> 
<script type="text/javascript"> 

function Particle(x, y) 
{ 
    this.position = new Vector(x, y); 
    this.velocity = new Vector(0.0, 0.0); 
    this.force = new Vector(0.0, 0.0); 
    this.mass = 1; 
    this.alpha = 0; 
} 

// Canvas 
var canvas = null; 
var context2D = null; 

// Blue Particle Texture 
var blueParticleTexture = new Image(); 
var blueParticleTextureLoaded = false; 

var blueParticleTextureAlpha = new Array(); 

var mousePosition = new Vector(); 
var mouseDownPosition = new Vector(); 

// Particles 
var particles = new Array(); 

var center = new Vector(250, 250); 

var imageData; 

function Initialize() 
{ 
    canvas = document.getElementById('canvas'); 
    context2D = canvas.getContext('2d'); 

    for (var createEntity = 0; createEntity < 150; ++createEntity) 
    { 
     var randomAngle = Math.random() * Math.PI * 2; 
     var particle = new Particle(Math.cos(randomAngle) * 250 + 250, Math.sin(randomAngle) * 250 + 250); 
     particle.velocity = center.Subtract(particle.position).Normal().Normalize().Multiply(Math.random() * 5 + 2); 
     particle.mass = Math.random() * 3 + 0.5; 
     particles.push(particle); 
    } 

    blueParticleTexture.onload = function() 
    { 
     context2D.drawImage(blueParticleTexture, 0, 0); 
     imageData = context2D.getImageData(0, 0, 5, 5); 
     var imageDataPixels = imageData.data; 
     for (var i = 0; i <= 255; ++i) 
     { 
      var newImageData = context2D.createImageData(5, 5); 
      var pixels = newImageData.data; 
      for (var j = 0, n = pixels.length; j < n; j += 4) 
      { 
       pixels[j] = imageDataPixels[j]; 
       pixels[j + 1] = imageDataPixels[j + 1]; 
       pixels[j + 2] = imageDataPixels[j + 2]; 
       pixels[j + 3] = Math.floor(imageDataPixels[j + 3] * i/255); 
      } 
      blueParticleTextureAlpha.push(newImageData); 
     } 
     blueParticleTextureLoaded = true; 
    } 
    blueParticleTexture.src = 'blueparticle.png'; 

    setInterval(Update, 50); 
} 

function Update() 
{ 
    // Clear the screen 
    context2D.clearRect(0, 0, canvas.width, canvas.height); 

    for (var i = 0; i < particles.length; ++i) 
    { 
     var particle = particles[i]; 

     var v = center.Subtract(particle.position).Normalize().Multiply(0.5); 
     particle.force = v; 
     particle.velocity.ThisAdd(particle.force.Divide(particle.mass)); 
     particle.velocity.ThisMultiply(0.98); 
     particle.position.ThisAdd(particle.velocity); 
     particle.force = new Vector(); 
     //if (particle.alpha + 5 < 255) particle.alpha += 5; 
     if (particle.position.Subtract(center).LengthSquared() < 20 * 20) 
     { 
      var randomAngle = Math.random() * Math.PI * 2; 
      particle.position = new Vector(Math.cos(randomAngle) * 250 + 250, Math.sin(randomAngle) * 250 + 250); 
      particle.velocity = center.Subtract(particle.position).Normal().Normalize().Multiply(Math.random() * 5 + 2); 
      //particle.alpha = 0; 
     } 
    } 

    if (blueParticleTextureLoaded) 
    { 
     for (var i = 0; i < particles.length; ++i) 
     { 
      var particle = particles[i]; 
      var intensity = Math.min(1, Math.max(0, 1 - Math.abs(particle.position.Subtract(center).Length() - 125)/125)); 
      context2D.putImageData(blueParticleTextureAlpha[Math.floor(intensity * 255)], particle.position.X - 2.5, particle.position.Y - 2.5, 0, 0, blueParticleTexture.width, blueParticleTexture.height); 
      //context2D.drawImage(blueParticleTexture, particle.position.X - 2.5, particle.position.Y - 2.5); 
     } 
    } 
} 

</script> 

<body onload="Initialize()" style="background-color:black"> 
    <canvas id="canvas" width="500" height="500" style="border:2px solid gray;"/> 
     <h1>Canvas is not supported in this browser.</h1> 
    </canvas> 
    <p>No directions</p> 
</body> 
</html> 

dove vector.js è solo un oggetto vettoriale ingenua:

// Vector class 

// TODO: EXamples 
// v0 = v1 * 100 + v3 * 200; 
// v0 = v1.MultiplY(100).Add(v2.MultiplY(200)); 

// TODO: In the future maYbe implement: 
// VectorEval("%1 = %2 * %3 + %4 * %5", v0, v1, 100, v2, 200); 

function Vector(X, Y) 
{ 
    /* 
    this.__defineGetter__("X", function() { return this.X; }); 
    this.__defineSetter__("X", function(value) { this.X = value }); 

    this.__defineGetter__("Y", function() { return this.Y; }); 
    this.__defineSetter__("Y", function(value) { this.Y = value }); 
    */ 

    this.Add = function(v) 
    { 
     return new Vector(this.X + v.X, this.Y + v.Y); 
    } 

    this.Subtract = function(v) 
    { 
     return new Vector(this.X - v.X, this.Y - v.Y); 
    } 

    this.Multiply = function(s) 
    { 
     return new Vector(this.X * s, this.Y * s); 
    } 

    this.Divide = function(s) 
    { 
     return new Vector(this.X/s, this.Y/s); 
    } 

    this.ThisAdd = function(v) 
    { 
     this.X += v.X; 
     this.Y += v.Y; 
     return this; 
    } 

    this.ThisSubtract = function(v) 
    { 
     this.X -= v.X; 
     this.Y -= v.Y; 
     return this; 
    } 

    this.ThisMultiply = function(s) 
    { 
     this.X *= s; 
     this.Y *= s; 
     return this; 
    } 

    this.ThisDivide = function(s) 
    { 
     this.X /= s; 
     this.Y /= s; 
     return this; 
    } 

    this.Length = function() 
    { 
     return Math.sqrt(this.X * this.X + this.Y * this.Y); 
    } 

    this.LengthSquared = function() 
    { 
     return this.X * this.X + this.Y * this.Y; 
    } 

    this.Normal = function() 
    { 
     return new Vector(-this.Y, this.X); 
    } 

    this.ThisNormal = function() 
    { 
     var X = this.X; 
     this.X = -this.Y 
     this.Y = X; 
     return this; 
    } 

    this.Normalize = function() 
    { 
     var length = this.Length(); 
     if(length != 0) 
     { 
      return new Vector(this.X/length, this.Y/length); 
     } 
    } 

    this.ThisNormalize = function() 
    { 
     var length = this.Length(); 
     if (length != 0) 
     { 
      this.X /= length; 
      this.Y /= length; 
     } 
     return this; 
    } 

    this.Negate = function() 
    { 
     return new Vector(-this.X, -this.Y); 
    } 

    this.ThisNegate = function() 
    { 
     this.X = -this.X; 
     this.Y = -this.Y; 
     return this; 
    } 

    this.Compare = function(v) 
    { 
     return Math.abs(this.X - v.X) < 0.0001 && Math.abs(this.Y - v.Y) < 0.0001; 
    } 

    this.Dot = function(v) 
    { 
     return this.X * v.X + this.Y * v.Y; 
    } 

    this.Cross = function(v) 
    { 
     return this.X * v.Y - this.Y * v.X; 
    } 

    this.Projection = function(v) 
    { 
     return this.MultiplY(v, (this.X * v.X + this.Y * v.Y)/(v.X * v.X + v.Y * v.Y)); 
    } 

    this.ThisProjection = function(v) 
    { 
     var temp = (this.X * v.X + this.Y * v.Y)/(v.X * v.X + v.Y * v.Y); 
     this.X = v.X * temp; 
     this.Y = v.Y * temp; 
     return this; 
    } 

    // If X and Y aren't supplied, default them to zero 
    if (X == undefined) this.X = 0; else this.X = X; 
    if (Y == undefined) this.Y = 0; else this.Y = Y; 
} 
/* 
Object.definePropertY(Vector, "X", {get : function(){ return X; }, 
           set : function(value){ X = value; }, 
           enumerable : true, 
           configurable : true}); 
Object.definePropertY(Vector, "Y", {get : function(){ return X; }, 
           set : function(value){ X = value; }, 
           enumerable : true, 
           configurable : true}); 
*/ 
+0

Grazie per la risposta, sembra una soluzione equa se si desidera ottenere buone prestazioni in questo momento, ma sto cercando il modo migliore per colorare le particelle al volo senza memorizzare alcun dato grafico. – djdolber

+0

Daremo un'occhiata a come tingete i dati grafici e provate quella soluzione per il momento, ma spero in una soluzione migliore che sia "incorporata" per così dire. – djdolber

+0

Anche se, userò il tuo metodo di colorazione ma per la colorazione in tempo reale. – djdolber

2

Questa domanda si è fermato. La soluzione che alcuni sembrano suggerire è quella di disegnare l'immagine da colorare su un'altra tela e da lì afferrare l'oggetto ImageData per poterlo modificare pixel per pixel, il problema con questo è che non è accettabile in un contesto di sviluppo del gioco perché fondamentalmente dovrò disegnare ogni particella 2 volte anziché 1. Una soluzione che sto per provare è quella di disegnare ogni particella una volta su una tela e afferrare l'oggetto ImageData, prima che l'applicazione vera inizi, e quindi lavorare con l'oggetto ImageData invece del vero oggetto Immagine ma potrebbe risultare costoso creare nuove copie poiché dovrò mantenere un oggetto ImageData originale non modificato per ogni immagine.

28

Hai operazioni di compositing e una di queste è la destinazione in cima. Se si compone un'immagine su un colore a tinta unita con 'context.globalCompositeOperation = "destination-atop"', avrà l'alfa dell'immagine in primo piano e il colore dell'immagine di sfondo. L'ho usato per fare una copia completamente colorata di un'immagine, e poi ho disegnato quella copia completamente colorata sopra l'originale con una opacità uguale alla quantità che voglio colorare.

Ecco il codice completo:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> 
<html> 
    <head> 
     <title>HTML5 Canvas Test</title> 
     <script type="text/javascript"> 
var x; //drawing context 
var width; 
var height; 
var fg; 
var buffer 

window.onload = function() { 
    var drawingCanvas = document.getElementById('myDrawing'); 
    // Check the element is in the DOM and the browser supports canvas 
    if(drawingCanvas && drawingCanvas.getContext) { 
     // Initaliase a 2-dimensional drawing context 
     x = drawingCanvas.getContext('2d'); 
     width = x.canvas.width; 
     height = x.canvas.height; 

     // grey box grid for transparency testing 
     x.fillStyle = '#666666'; 
     x.fillRect(0,0,width,height); 
     x.fillStyle = '#AAAAAA'; 
     var i,j; 
     for (i=0; i<100; i++){ 
      for (j=0; j<100; j++){ 
       if ((i+j)%2==0){ 
        x.fillRect(20*i,20*j,20,20); 
       } 
      } 
     } 

     fg = new Image(); 
     fg.src = 'http://uncc.ath.cx/LayerCake/images/16/3.png'; 

     // create offscreen buffer, 
     buffer = document.createElement('canvas'); 
     buffer.width = fg.width; 
     buffer.height = fg.height; 
     bx = buffer.getContext('2d'); 

     // fill offscreen buffer with the tint color 
     bx.fillStyle = '#FF0000' 
     bx.fillRect(0,0,buffer.width,buffer.height); 

     // destination atop makes a result with an alpha channel identical to fg, but with all pixels retaining their original color *as far as I can tell* 
     bx.globalCompositeOperation = "destination-atop"; 
     bx.drawImage(fg,0,0); 


     // to tint the image, draw it first 
     x.drawImage(fg,0,0); 

     //then set the global alpha to the amound that you want to tint it, and draw the buffer directly on top of it. 
     x.globalAlpha = 0.5; 
     x.drawImage(buffer,0,0); 
    } 
} 
     </script> 
    </head> 
    </body> 
     <canvas id="myDrawing" width="770" height="400"> 
      <p>Your browser doesn't support canvas.</p> 
     </canvas> 
    </body> 
</html> 
+0

Questo ha funzionato alla grande per me! Grazie mille. – rhigdon

+0

Qualcuno sa se questo ha prestazioni "da gioco"? Immagino sia meglio creare il buffer una volta e poi riutilizzarlo? –

1

vorrei dare un'occhiata a questo: http://www.arahaya.com/canvasscript3/examples/ sembra avere un metodo ColorTransform, credo che sta disegnando una forma di fare la trasformazione, ma forse sulla base di questa puoi trovare un modo per regolare un'immagine specifica.

4

C'è un metodo here che è possibile utilizzare per colorare le immagini, ed è più accurato quindi disegnare rettangoli colorati e più velocemente, quindi lavorare su base pixel per pixel. Una spiegazione completa è in quel post del blog, incluso il codice JS, ma ecco un riassunto di come funziona.

Prima di tutto si passa all'immagine che si sta colorando pixel per pixel, leggendo i dati e suddividendo ciascun pixel in 4 componenti separati: rosso, verde, blu e nero.Scrivi ogni componente su una tela separata. Quindi ora hai 4 versioni (rossa, verde, blu e nera) dell'immagine originale.

Quando si desidera disegnare un'immagine colorata, si crea (o si trova) una tela fuori schermo e si disegnano questi componenti. Il nero viene disegnato per primo, e quindi è necessario impostare l'opzione GlobalCompositeOperation del canvas su "light" in modo che i componenti successivi vengano aggiunti al canvas. Il nero è anche non trasparente.

I seguenti tre componenti vengono disegnati (le immagini rosse, blu e verdi), ma il loro valore alfa si basa sulla quantità del loro componente che compone il colore del disegno. Quindi se il colore è bianco, tutti e tre vengono disegnati con 1 alfa. Se il colore è verde, viene disegnata solo l'immagine verde e gli altri due vengono saltati. Se il colore è arancione, hai il rosso alfa completo, disegna il verde parzialmente trasparente e salta il blu.

Ora hai una versione colorata della tua immagine resa sulla tela di riserva, e la disegni su dove mai ti serve sulla tela.

Ancora una volta il codice per farlo è nel post del blog.

0

Sfortunatamente, non c'è un solo valore da modificare simile alle librerie OpenGL o DirectX che ho usato in passato. Tuttavia, non è troppo lavoro per creare una nuova area di memoria buffer e utilizzare lo globalCompositeOperation disponibile per disegnare un'immagine.

// Create a buffer element to draw based on the Image img 
let el = Object.assign(document.createElement('canvas'), { 
    width: img.width, 
    height: img.height 
}); 
let btx = el.getContext('2d'); 

// First draw your image to the buffer 
btx.drawImage(img, 0, 0); 

// Now we'll multiply a rectangle of your chosen color 
btx.fillStyle = '#FF7700'; 
btx.globalCompositeOperation = 'multiply'; 
btx.fillRect(0, 0, el.width, el.height); 

// Finally, fix masking issues you'll probably incur and optional globalAlpha 
btx.globalAlpha = 0.5; 
btx.globalCompositeOperation = 'destination-in'; 
btx.drawImage(img, 0, 0); 

È ora possibile utilizzare el come primo parametro canvas2d.drawImage. Utilizzando moltiplicare avrai letteralmente tinta ma tonalità e colore potrebbe anche essere di tuo gradimento. Inoltre, questo è abbastanza veloce da avvolgere in una funzione da riutilizzare.