Modificare il valore alfa di un'immagine con buffer?
-
20-08-2019 - |
Domanda
Come posso modificare il valore alfa globale di una BufferedImage in Java? (I.E. rende ogni pixel nell'immagine che ha un valore alfa di 100 un valore alfa di 80)
Soluzione
Non credo ci sia un singolo semplice comando per farlo. Alcune opzioni:
- copia in un'altra immagine con un AlphaComposite specificato (aspetto negativo: non convertito in atto)
- manipola direttamente il raster (aspetto negativo: può portare a immagini non gestite )
- utilizza un filtro o BufferedImageOp
Il primo è il più semplice da implementare, IMO.
Altri suggerimenti
@Neil Coffey: Grazie, ho cercato anche questo; tuttavia, il tuo codice non ha funzionato molto bene per me (lo sfondo bianco è diventato nero).
Ho codificato qualcosa del genere e funziona perfettamente:
public void setAlpha(byte alpha) {
alpha %= 0xff;
for (int cx=0;cx<obj_img.getWidth();cx++) {
for (int cy=0;cy<obj_img.getHeight();cy++) {
int color = obj_img.getRGB(cx, cy);
int mc = (alpha << 24) | 0x00ffffff;
int newcolor = color & mc;
obj_img.setRGB(cx, cy, newcolor);
}
}
}
Dove obj_img è BufferedImage.TYPE_INT_ARGB.
Cambio alpha con setAlpha ((byte) 125); l'intervallo alfa ora è 0-255.
Spero che qualcuno lo trovi utile.
Questa è una vecchia domanda, quindi non sto rispondendo per il bene del PO, ma per quelli come me che troveranno questa domanda in seguito.
AlphaComposite
Come menzionato l'eccellente schema di @ Michael, un'operazione AlphaComposite può modificare il canale alfa. Ma solo in certi modi, che per me sono in qualche modo difficili da capire:
è la formula per come il " oltre " l'operazione influisce sul canale alfa. Inoltre, ciò influisce anche sui canali RGB, quindi se hai dati cromatici che devono essere invariati, AlphaComposite non è la risposta.
BufferedImageOps
LookupOp
Esistono diverse varietà di BufferedImageOp (vedi 4.10.6 qui ). Nel caso più generale, l'attività dell'OP potrebbe essere soddisfatta da un LookupOp , che richiede la creazione di array di ricerca. Per modificare solo il canale alfa, fornire un array di identità (un array in cui la tabella [i] = i) per i canali RGB e un array separato per il canale alfa. Popolare l'ultimo array con table[i] = f(i)
, dove f()
è la funzione con cui si desidera eseguire il mapping dal vecchio valore alfa al nuovo. Per esempio. se vuoi " fai in modo che ogni pixel nell'immagine che abbia un valore alfa di 100 abbia un valore alfa di 80 " ;, imposta table[100] = 80
. (L'intera gamma va da 0 a 255.) Vedi come aumentare l'opacità nella sfocatura gaussiana per un esempio di codice.
RescaleOp
Ma per un sottoinsieme di questi casi, esiste un modo più semplice per farlo, che non richiede l'impostazione di una tabella di ricerca. Se newAlpha = oldAlpha - 20
è una funzione semplice e lineare, utilizzare un RescaleOp . Ad esempio, se si desidera impostare newAlpha = oldAlpha * 0.8
, utilizzare un RescaleOp con un fattore di scala 1 e un offset di -20. Se si desidera impostare <=>, utilizzare un fattore di scala di 0,8 e un offset di 0. In entrambi i casi, è necessario fornire nuovamente fattori di scala fittizi e offset per i canali RGB:
new RescaleOp({1.0f, 1.0f, 1.0f, /* alpha scaleFactor */ 0.8f},
{0f, 0f, 0f, /* alpha offset */ -20f}, null)
Ancora una volta 4.10.6 qui per alcuni esempi che illustrano bene i principi, ma non sono specifici del canale alfa.
Entrambi RescaleOp e LookupOp consentire modifica di un'immagine bufferizzata sul posto.
per un effetto di cambio alfa dall'aspetto più gradevole, puoi usare il cambio alfa relativo per pixel (piuttosto che un set statico o un ritaglio lineare)
public static void modAlpha(BufferedImage modMe, double modAmount) {
//
for (int x = 0; x < modMe.getWidth(); x++) {
for (int y = 0; y < modMe.getHeight(); y++) {
//
int argb = modMe.getRGB(x, y); //always returns TYPE_INT_ARGB
int alpha = (argb >> 24) & 0xff; //isolate alpha
alpha *= modAmount; //similar distortion to tape saturation (has scrunching effect, eliminates clipping)
alpha &= 0xff; //keeps alpha in 0-255 range
argb &= 0x00ffffff; //remove old alpha info
argb |= (alpha << 24); //add new alpha info
modMe.setRGB(x, y, argb);
}
}
}
Sono sicuro al 99% dei metodi che affermano di trattare un " RGB " valore racchiuso in un int realmente trattare con ARGB. Quindi dovresti essere in grado di fare qualcosa del tipo:
for (all x,y values of image) {
int argb = img.getRGB(x, y);
int oldAlpha = (argb >>> 24);
if (oldAlpha == 100) {
argb = (80 << 24) | (argb & 0xffffff);
img.setRGB(x, y, argb);
}
}
Per velocità, potresti forse usare i metodi per recuperare blocchi di valori di pixel.
Potrebbe essere necessario copiare prima l'immagine bufferizzata in un'immagine di tipo BufferedImage.TYPE_INT_ARGB
. Se l'immagine è di tipo, ad esempio BufferedImage.TYPE_INT_RGB
, il componente alfa non verrà impostato correttamente. Se BufferedImage è di tipo
/**
* Modifies each pixel of the BufferedImage so that the selected component (R, G, B, or A)
* is adjusted by delta. Note: the BufferedImage must be of type BufferedImage.TYPE_INT_ARGB.
* @param src BufferedImage of type BufferedImage.TYPE_INT_ARGB.
* @param colorIndex 0=red, 1=green, 2=blue, 3= alpha
* @param delta amount to change component
* @return
*/
public static BufferedImage adjustAColor(BufferedImage src,int colorIndex, int delta) {
int w = src.getWidth();
int h = src.getHeight();
assert(src.getType()==BufferedImage.TYPE_INT_ARGB);
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++) {
int rgb = src.getRGB(x,y);
java.awt.Color color= new java.awt.Color(rgb,true);
int red=color.getRed();
int green=color.getGreen();
int blue=color.getBlue();
int alpha=color.getAlpha();
switch (colorIndex) {
case 0: red=adjustColor(red,delta); break;
case 1: green=adjustColor(green,delta); break;
case 2: blue=adjustColor(blue,delta); break;
case 3: alpha=adjustColor(alpha,delta); break;
default: throw new IllegalStateException();
}
java.awt.Color adjustedColor=new java.awt.Color(red,green,blue,alpha);
src.setRGB(x,y,adjustedColor.getRGB());
int gottenColorInt=src.getRGB(x,y);
java.awt.Color gottenColor=new java.awt.Color(gottenColorInt,true);
assert(gottenColor.getRed()== red);
assert(gottenColor.getGreen()== green);
assert(gottenColor.getBlue()== blue);
assert(gottenColor.getAlpha()== alpha);
}
return src;
}
private static int adjustColor(int value255, int delta) {
value255+= delta;
if (value255<0) {
value255=0;
} else if (value255>255) {
value255=255;
}
return value255;
}
, il codice seguente funziona.
<=>