gvsig-raster / org.gvsig.raster / trunk / org.gvsig.raster / org.gvsig.raster.lib / org.gvsig.raster.lib.impl / src / main / java / org / gvsig / raster / impl / store / properties / DataStoreTransparency.java @ 2443
History | View | Annotate | Download (16.1 KB)
1 |
/* gvSIG. Geographic Information System of the Valencian Government
|
---|---|
2 |
*
|
3 |
* Copyright (C) 2007-2008 Infrastructures and Transports Department
|
4 |
* of the Valencian Government (CIT)
|
5 |
*
|
6 |
* This program is free software; you can redistribute it and/or
|
7 |
* modify it under the terms of the GNU General Public License
|
8 |
* as published by the Free Software Foundation; either version 2
|
9 |
* of the License, or (at your option) any later version.
|
10 |
*
|
11 |
* This program is distributed in the hope that it will be useful,
|
12 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14 |
* GNU General Public License for more details.
|
15 |
*
|
16 |
* You should have received a copy of the GNU General Public License
|
17 |
* along with this program; if not, write to the Free Software
|
18 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
19 |
* MA 02110-1301, USA.
|
20 |
*
|
21 |
*/
|
22 |
package org.gvsig.raster.impl.store.properties; |
23 |
|
24 |
import java.util.ArrayList; |
25 |
import java.util.List; |
26 |
|
27 |
import org.gvsig.fmap.dal.coverage.dataset.Buffer; |
28 |
import org.gvsig.fmap.dal.coverage.datastruct.NoData; |
29 |
import org.gvsig.fmap.dal.coverage.datastruct.TransparencyRange; |
30 |
import org.gvsig.fmap.dal.coverage.store.props.ColorInterpretation; |
31 |
import org.gvsig.fmap.dal.coverage.store.props.Transparency; |
32 |
import org.gvsig.fmap.dal.coverage.util.PropertyEvent; |
33 |
import org.gvsig.fmap.dal.coverage.util.PropertyListener; |
34 |
import org.gvsig.tools.ToolsLocator; |
35 |
import org.gvsig.tools.dynobject.DynStruct; |
36 |
import org.gvsig.tools.persistence.PersistenceManager; |
37 |
import org.gvsig.tools.persistence.Persistent; |
38 |
import org.gvsig.tools.persistence.PersistentState; |
39 |
import org.gvsig.tools.persistence.exception.PersistenceException; |
40 |
/**
|
41 |
* <p>
|
42 |
* Esta clase contiene informaci?n de transparencia de un objeto. Los objetos
|
43 |
* pueden ser dataset, grid u otros. Cuando un usuario quiere a?adir nueva
|
44 |
* informaci?n de transparencia crea un objeto de este tipo que debe ser
|
45 |
* mezclado (merge) con otros objetos de este tipo que existan para la misma
|
46 |
* fuente de datos.
|
47 |
* </p>
|
48 |
* <p>
|
49 |
* Un multirasterdatset obtiene los distintos objetos transparency de todos los
|
50 |
* ficheros que los componen. Para realizar un solo objeto transparency se har?
|
51 |
* un merge de todos ellos.
|
52 |
* </p>
|
53 |
* <p>
|
54 |
* Finalmente y antes de renderizar se necesita un objeto Transparency. Este
|
55 |
* estar? compuesto con toda la informaci?n de transparencia aplicar, es decir,
|
56 |
* todos los objetos Transparency mezclados.Transparency con tiene el
|
57 |
* m?todo de procesado de un pixel. Se le proporciona un pixel y devuelve este
|
58 |
* mismo pixel con la transparencia aplicada.
|
59 |
* </p>
|
60 |
* <p>
|
61 |
* Una transparencia que se aplica a un buffer puede tener cuatro procedencias distintas:
|
62 |
* <UL>
|
63 |
* <LI>Mascara: Un buffer de NxN aplicado sobre la zona a renderizar como una m?scara de
|
64 |
* transparencia. Las bandas alpha de las imagenes se comportan de esta forma.</LI>
|
65 |
* <LI>Opacidad global: Un valor de opacidad se aplicado a cada pixel a renderizar. Este es
|
66 |
* igual para todos los p?xeles.</LI>
|
67 |
* <LI>Rangos: Una lista de rangos de valores RGB. Cada pixel cuyo valor est? dentro de alguno
|
68 |
* de los rangos se aplica como transparente.</LI>
|
69 |
* <LI>Dato: Todos los valores del buffer que coincidan con ese dato son puestos como transparente.
|
70 |
* Para que esto sea posible tenemos que disponer del buffer de datos originales sin ning?n proceso.</LI>
|
71 |
* </UL>
|
72 |
* </p>
|
73 |
*
|
74 |
* @author Nacho Brodin (nachobrodin@gmail.com)
|
75 |
*/
|
76 |
public class DataStoreTransparency implements Transparency, Persistent { |
77 |
public static int MAX_OPACITY = 255; |
78 |
|
79 |
/**
|
80 |
* Alpha source
|
81 |
*/
|
82 |
protected ColorInterpretation
|
83 |
colorInterpretation = null;
|
84 |
/**
|
85 |
* Buffer con los datos originales (sin filtrar) correspondiente a la zona a renderizar.
|
86 |
* Esto es util para aplicar el valor NoData ya que hay que consultar el valor original del
|
87 |
* dato. Despu?s de hacer un process es recomendable hacer free para poner a null los buffers.
|
88 |
*/
|
89 |
protected Buffer originalData = null; |
90 |
/**
|
91 |
* Valor de dato transparente. Todos los p?xeles de originalData que correspondan con este
|
92 |
* valor se pondr?n 100% transparentes.
|
93 |
*/
|
94 |
protected NoData noData = null; |
95 |
|
96 |
/**
|
97 |
* Rangos de transparencia aplicados. Lista de TransparencyRange
|
98 |
*/
|
99 |
protected List<TransparencyRange> |
100 |
transparencyRanges = new ArrayList<TransparencyRange>(); |
101 |
/**
|
102 |
* Grado de opacidad de todo el raster
|
103 |
*/
|
104 |
protected int opacity = 0xff; |
105 |
|
106 |
/**
|
107 |
* Array de listeners que ser?n informados cuando cambia la propiedad de transparencia
|
108 |
*/
|
109 |
private List<PropertyListener> |
110 |
transparencyPropertyListener = new ArrayList<PropertyListener>(); |
111 |
/**
|
112 |
* Flag de activaci?n de la transparencia cuando se visualiza o exporta.
|
113 |
*/
|
114 |
private boolean transparencyActive = false; |
115 |
|
116 |
public DataStoreTransparency() {}
|
117 |
|
118 |
/**
|
119 |
* Constructor
|
120 |
*/
|
121 |
public DataStoreTransparency(ColorInterpretation colorInterpretation) {
|
122 |
this.colorInterpretation = colorInterpretation;
|
123 |
} |
124 |
|
125 |
public static void mergeBuffers(Buffer src1, Buffer src2) { |
126 |
for (int y = 0; y < src1.getHeight(); y++) |
127 |
for (int x = 0; x < src1.getWidth(); x++) |
128 |
// ((a / 255) * (b / 255)) * 255
|
129 |
// Es lo mismo que:
|
130 |
// (a * b) / 255
|
131 |
src1.setElem(y, x, 0,
|
132 |
(byte) (((src1.getElemByte(y, x, 0) & 0xff) * (src2.getElemByte(y, x, 0) & 0xff)) / 255D)); |
133 |
} |
134 |
|
135 |
public void addPropertyListener(PropertyListener listener) { |
136 |
transparencyPropertyListener.add(listener); |
137 |
} |
138 |
|
139 |
/**
|
140 |
* M?todo llamado cuando hay un cambio en una propiedad de transparencia
|
141 |
*/
|
142 |
private void callPropertyChanged(Object obj) { |
143 |
for (int i = 0; i < transparencyPropertyListener.size(); i++) { |
144 |
PropertyEvent ev = new PropertyEvent(this, "transparency", null, null); |
145 |
((PropertyListener)transparencyPropertyListener.get(i)).actionValueChanged(ev); |
146 |
} |
147 |
} |
148 |
|
149 |
public boolean existAlphaBand() { |
150 |
return getAlphaBandNumber() >= 0; |
151 |
} |
152 |
|
153 |
/**
|
154 |
* Obtiene el ?rea de datos
|
155 |
* @return M?scara de transparencia
|
156 |
*/
|
157 |
public Buffer getDataBuffer() { |
158 |
return originalData;
|
159 |
} |
160 |
|
161 |
public void setDataBuffer(Buffer b) { |
162 |
originalData = b; |
163 |
} |
164 |
|
165 |
/**
|
166 |
* Obtiene el valor noData
|
167 |
* @return
|
168 |
*/
|
169 |
public NoData getNoData() {
|
170 |
return noData;
|
171 |
} |
172 |
|
173 |
public void setNoData(NoData noData) { |
174 |
if(this.noData != noData) |
175 |
callPropertyChanged(this);
|
176 |
this.noData = noData;
|
177 |
} |
178 |
|
179 |
/**
|
180 |
* Obtiene los rangos de pixels que son transparentes en el raster.
|
181 |
* @return Rangos de transparencias a aplicar
|
182 |
*/
|
183 |
public List<TransparencyRange> getTransparencyRange() { |
184 |
return transparencyRanges;
|
185 |
} |
186 |
|
187 |
public void setTransparencyRange(TransparencyRange range) { |
188 |
this.transparencyRanges.add(range);
|
189 |
callPropertyChanged(this);
|
190 |
} |
191 |
|
192 |
/**
|
193 |
* Asigna la lista de rangos de transparencia
|
194 |
* @param ranges
|
195 |
*/
|
196 |
public void setTransparencyRangeList(List<TransparencyRange> ranges) { |
197 |
this.transparencyRanges = ranges;
|
198 |
callPropertyChanged(this);
|
199 |
} |
200 |
|
201 |
public void clearListOfTransparencyRange() { |
202 |
transparencyRanges.clear(); |
203 |
callPropertyChanged(this);
|
204 |
} |
205 |
|
206 |
/**
|
207 |
* Obtiene el grado de opacidad de todo el raster
|
208 |
* @return valor del grado de opacidad.
|
209 |
*/
|
210 |
public int getOpacity() { |
211 |
return opacity;
|
212 |
} |
213 |
|
214 |
public void setOpacity(int opacity) { |
215 |
if(opacity != this.opacity) |
216 |
callPropertyChanged(this);
|
217 |
this.opacity = opacity;
|
218 |
} |
219 |
|
220 |
/**
|
221 |
* Asigna la transparencia a partir de un objeto con los metadatos del raster.
|
222 |
* @param metadata
|
223 |
*/
|
224 |
public void setTransparencyByPixelFromMetadata(DataStoreMetadata metadata){ |
225 |
if (metadata != null) { |
226 |
TransparencyRange[] noData = metadata.parserNodataInMetadata();
|
227 |
if (noData != null) |
228 |
for (int i = 0; i < noData.length; i++) |
229 |
getTransparencyRange().add(noData[i]); |
230 |
TransparencyRange noDataValue = metadata.parserNodataByBand(); |
231 |
if (noData == null && noDataValue != null) |
232 |
getTransparencyRange().add(noDataValue); |
233 |
} |
234 |
} |
235 |
|
236 |
/**
|
237 |
* Obtiene la banda de transpareci si existe o -1 si no existe.
|
238 |
* @return n?mero de banda de transparencia o -1 si no existe.
|
239 |
*/
|
240 |
public int getAlphaBandNumber() { |
241 |
return colorInterpretation.getAlphaBand();
|
242 |
} |
243 |
|
244 |
/**
|
245 |
* Asigna la informaci?n de si existe o no banda de transparencia cuando este
|
246 |
* objeto va asociado a un dataset. Si tiene este tipo de banda en cada
|
247 |
* dibujado se cargar? la informaci?n de m?scara de transparencia en el la
|
248 |
* variable mask.
|
249 |
* @param true si existe banda de transparencia y false si no lo es.
|
250 |
*/
|
251 |
public void setTransparencyBand(int alphaBandNumber) { |
252 |
colorInterpretation.setColorInterpValue(alphaBandNumber, ColorInterpretation.ALPHA_BAND); |
253 |
} |
254 |
|
255 |
/**
|
256 |
* Consulta si el valor de la posici?n (line, col) del buffer es considerado
|
257 |
* NoData o no.
|
258 |
* @param line Linea del buffer
|
259 |
* @param col Columna del buffer
|
260 |
* @return
|
261 |
*/
|
262 |
protected boolean isNoData(int line, int col) { |
263 |
if(noData == null) |
264 |
return false; |
265 |
|
266 |
if(noData.isDefined()) {
|
267 |
for (int iBand = 0; iBand < originalData.getBandCount(); iBand++) { |
268 |
switch (originalData.getDataType()) {
|
269 |
case Buffer.TYPE_BYTE: |
270 |
if(originalData.getElemByte(line, col, iBand) == noData.getValue().byteValue())
|
271 |
return true; |
272 |
break;
|
273 |
case Buffer.TYPE_SHORT: |
274 |
if(originalData.getElemShort(line, col, iBand) == noData.getValue().shortValue())
|
275 |
return true; |
276 |
break;
|
277 |
case Buffer.TYPE_INT: |
278 |
if(originalData.getElemInt(line, col, iBand) == noData.getValue().intValue())
|
279 |
return true; |
280 |
break;
|
281 |
case Buffer.TYPE_FLOAT: |
282 |
float f = originalData.getElemFloat(line, col, iBand);
|
283 |
if((f == noData.getValue().floatValue()) || Float.isNaN(f)) |
284 |
return true; |
285 |
break;
|
286 |
case Buffer.TYPE_DOUBLE: |
287 |
double d = originalData.getElemDouble(line, col, iBand);
|
288 |
if((d == noData.getValue().doubleValue()) || Double.isNaN(d)) |
289 |
return true; |
290 |
break;
|
291 |
} |
292 |
} |
293 |
} |
294 |
|
295 |
return false; |
296 |
} |
297 |
|
298 |
public void activeTransparency() { |
299 |
if( existAlphaBand() ||
|
300 |
(getNoData() != null && getNoData().isDefined() && getNoData().isNoDataTransparent()) ||
|
301 |
(transparencyRanges.size() > 0) ||
|
302 |
(opacity != 0xff))
|
303 |
transparencyActive = true;
|
304 |
else
|
305 |
transparencyActive = false;
|
306 |
} |
307 |
|
308 |
public boolean isTransparencyActive() { |
309 |
return transparencyActive;
|
310 |
} |
311 |
|
312 |
/**
|
313 |
* Asigna el flag de transparencia activa o desactivada.
|
314 |
* @param transparencyActive true activa la transparencia false la desactiva
|
315 |
*/
|
316 |
public void setTransparencyActive(boolean transparencyActive) { |
317 |
this.transparencyActive = transparencyActive;
|
318 |
} |
319 |
|
320 |
public int processRGB(int r, int g, int b, int line, int col, Buffer buf) { |
321 |
// Si el valor es noData se pone totalmente transparente y ya no se tiene en
|
322 |
// cuenta nada m?s.
|
323 |
if (originalData != null && (getNoData().isDefined() && getNoData().isNoDataTransparent())) { |
324 |
if (isNoData(line, col))
|
325 |
// El alpha no se pone para dejarlo a 0, totalmente transparente
|
326 |
return (r << 16 | g << 8 | b); |
327 |
} |
328 |
|
329 |
/**
|
330 |
* Para sacar el valor del nuevo alpha, lo que hacemos es convertir a
|
331 |
* porcentajes todos los alphas posibles (en porcentajes) y multiplicarlos
|
332 |
* entre si. Con esto conseguimos un nuevo porcentaje de alpha entre 0 y 1.
|
333 |
* Una vez hecho esto se multiplica por 255 para tenerlo dentro del rango
|
334 |
* deseado. Como queremos evitar operaciones de m?s, podemos quitar una
|
335 |
* division de 255 y as? nos ahorramos luego multiplicarlo por 255 otra vez.
|
336 |
*/
|
337 |
|
338 |
// Quitada la division para optimizar
|
339 |
//double a = opacity / 255D;
|
340 |
double a = opacity;
|
341 |
|
342 |
int alphaRange = processRange(r, g, b);
|
343 |
if (alphaRange != 255) |
344 |
a *= (alphaRange / 255D);
|
345 |
|
346 |
if (existAlphaBand() && buf != null) |
347 |
a *= (buf.getElemByte(line, col, getAlphaBandNumber()) & 0xff) / 255D; |
348 |
|
349 |
// Quitada la multiplicacion para optimizar
|
350 |
// a = (int)(a * 255D);
|
351 |
|
352 |
return ((int) a << 24) | r << 16 | g << 8 | b; |
353 |
} |
354 |
|
355 |
/**
|
356 |
* Aplica la transparecia por rangos al pixel pasado por par?metro. El valor
|
357 |
* en la posici?n cero es el alpha por lo tanto ser? esta posici?n la que se
|
358 |
* modificar?.
|
359 |
* @param r
|
360 |
* @param g
|
361 |
* @param b
|
362 |
* @return Un valor de 0 a 255. Donde 255 es totalmente opaco
|
363 |
*/
|
364 |
private int processRange(int r, int g, int b) { |
365 |
for (int i = 0; i < transparencyRanges.size(); i++) { |
366 |
TransparencyRange tr = ((TransparencyRange) transparencyRanges.get(i)); |
367 |
if (tr == null) continue; |
368 |
if (tr.isAnd()) {
|
369 |
if (tr.getRed() == null || |
370 |
tr.getGreen() == null ||
|
371 |
tr.getBlue() == null ||
|
372 |
r < tr.getRed()[0] || r > tr.getRed()[1] || |
373 |
g < tr.getGreen()[0] || g > tr.getGreen()[1] || |
374 |
b < tr.getBlue()[0] || b > tr.getBlue()[1]) |
375 |
continue;
|
376 |
|
377 |
return tr.getAlpha();
|
378 |
} else {
|
379 |
if (tr.getRed() != null) { |
380 |
if (r >= tr.getRed()[0] && r <= tr.getRed()[1]) { |
381 |
return tr.getAlpha();
|
382 |
} |
383 |
} |
384 |
if (tr.getGreen() != null) { |
385 |
if (g >= tr.getGreen()[0] && g <= tr.getGreen()[1]) { |
386 |
return tr.getAlpha();
|
387 |
} |
388 |
} |
389 |
if (tr.getBlue() != null) { |
390 |
if (b >= tr.getBlue()[0] && b <= tr.getBlue()[1]) { |
391 |
return tr.getAlpha();
|
392 |
} |
393 |
} |
394 |
} |
395 |
} |
396 |
return 255; |
397 |
} |
398 |
|
399 |
public void setColorInterpretation(ColorInterpretation colorInterpretation) { |
400 |
this.colorInterpretation = colorInterpretation;
|
401 |
} |
402 |
|
403 |
@SuppressWarnings("unchecked") |
404 |
public Transparency cloneTransparency() { |
405 |
DataStoreTransparency t = new DataStoreTransparency(this.colorInterpretation.cloneColorInterpretation()); |
406 |
|
407 |
//TODO: FUNCIONALIDAD: Falta asignar lo necesario para la transparencia por selecci?n
|
408 |
t.transparencyRanges = (ArrayList<TransparencyRange>)((ArrayList<TransparencyRange>) getTransparencyRange()).clone(); |
409 |
//t.mask = getAlphaBand();
|
410 |
t.opacity = getOpacity(); |
411 |
t.noData = getNoData(); |
412 |
t.originalData = originalData; |
413 |
t.transparencyActive = transparencyActive; |
414 |
t.activeTransparency(); |
415 |
return t;
|
416 |
} |
417 |
|
418 |
@SuppressWarnings("unchecked") |
419 |
public void loadFromState(PersistentState state) |
420 |
throws PersistenceException {
|
421 |
this.colorInterpretation = (ColorInterpretation)state.get("colorinterpretation"); |
422 |
|
423 |
if (state.hasValue("opacity")) { |
424 |
opacity = state.getInt("opacity");
|
425 |
} |
426 |
|
427 |
List<TransparencyRange> tr = state.getList("transparencyRange"); |
428 |
if(tr != null) { |
429 |
transparencyRanges = new ArrayList<TransparencyRange>(tr); |
430 |
if (state.hasValue("bandnumber")) { |
431 |
int alphaBandNumber = state.getInt("bandnumber"); |
432 |
setTransparencyBand(alphaBandNumber); |
433 |
} |
434 |
} |
435 |
|
436 |
this.transparencyActive = state.getBoolean("transparencyActive"); |
437 |
//this.noData = (NoData)state.get("noData");
|
438 |
} |
439 |
|
440 |
public void saveToState(PersistentState state) throws PersistenceException { |
441 |
state.set("colorinterpretation", colorInterpretation);
|
442 |
if(getOpacity() != 255) { |
443 |
state.set("opacity", getOpacity());
|
444 |
} |
445 |
|
446 |
//Rangos de transparencia
|
447 |
state.set("transparencyRange", transparencyRanges);
|
448 |
|
449 |
if(getAlphaBandNumber() != -1) { |
450 |
state.set("bandnumber", getAlphaBandNumber());
|
451 |
} |
452 |
|
453 |
state.set("transparencyActive", transparencyActive);
|
454 |
//state.set("noData", noData);
|
455 |
} |
456 |
|
457 |
public static void registerPersistence() { |
458 |
PersistenceManager manager = ToolsLocator.getPersistenceManager(); |
459 |
DynStruct definition = manager.getDefinition("Transparency_Persistent");
|
460 |
if( definition == null ) { |
461 |
definition = manager.addDefinition( |
462 |
DataStoreTransparency.class, |
463 |
"Transparency_Persistent",
|
464 |
"Transparency Persistent definition (FIXME)",
|
465 |
null,
|
466 |
null
|
467 |
); |
468 |
|
469 |
definition.addDynFieldInt("opacity")
|
470 |
.setMandatory(false);
|
471 |
|
472 |
definition.addDynFieldInt("bandnumber")
|
473 |
.setMandatory(false);
|
474 |
|
475 |
definition.addDynFieldList("transparencyRange")
|
476 |
.setClassOfItems(TransparencyRange.class) |
477 |
.setMandatory(false);
|
478 |
|
479 |
definition.addDynFieldBoolean("transparencyActive")
|
480 |
.setMandatory(false);
|
481 |
|
482 |
definition.addDynFieldObject("colorinterpretation")
|
483 |
.setClassOfValue(ColorInterpretation.class) |
484 |
.setMandatory(false);
|
485 |
|
486 |
/*definition.addDynFieldDouble("noData")
|
487 |
.setMandatory(false);*/
|
488 |
} |
489 |
} |
490 |
|
491 |
public void dispose() { |
492 |
if (originalData != null) |
493 |
originalData.dispose(); |
494 |
originalData = null;
|
495 |
} |
496 |
|
497 |
public ColorInterpretation getColorInterpretation() {
|
498 |
return colorInterpretation;
|
499 |
} |
500 |
|
501 |
protected void finalize() throws Throwable { |
502 |
noData = null;
|
503 |
if(transparencyPropertyListener != null) { |
504 |
transparencyPropertyListener.clear(); |
505 |
transparencyPropertyListener = null;
|
506 |
} |
507 |
if(transparencyRanges != null) { |
508 |
transparencyRanges.clear(); |
509 |
transparencyRanges = null;
|
510 |
} |
511 |
super.finalize();
|
512 |
} |
513 |
|
514 |
} |