Statistics
| Revision:

gvsig-raster / org.gvsig.raster / branches / org.gvsig.raster_dataaccess_refactoring / org.gvsig.raster.lib / org.gvsig.raster.lib.impl / src / main / java / org / gvsig / raster / impl / store / properties / DataStoreTransparency.java @ 2308

History | View | Annotate | Download (15.7 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 final String PERSISTENCE_NAME = "Transparency_Persistent";
78

    
79
        public static int     MAX_OPACITY           = 255;
80

    
81
        /**
82
         * Alpha source
83
         */
84
        protected ColorInterpretation
85
                              colorInterpretation   = null;
86
        /**
87
         * Buffer con los datos originales (sin filtrar) correspondiente a la zona a renderizar.
88
         * Esto es util para aplicar el valor NoData ya que hay que consultar el valor original del
89
         * dato. Despu?s de hacer un process es recomendable hacer free para poner a null los buffers.
90
         */
91
        protected Buffer     originalData           = null;
92
        /**
93
         * Valor de dato transparente. Todos los p?xeles de originalData que correspondan con este
94
         * valor se pondr?n 100% transparentes.
95
         */
96
        protected NoData     noData                 = null;
97

    
98
        /**
99
         * Rangos de transparencia aplicados. Lista de TransparencyRange
100
         */
101
        protected List<TransparencyRange>   
102
                              transparencyRanges    = new ArrayList<TransparencyRange>();
103
        /**
104
         * Grado de opacidad de todo el raster
105
         */
106
        protected int         opacity               = 0xff;
107

    
108
        /**
109
         * Array de listeners que ser?n informados cuando cambia la propiedad de transparencia
110
         */
111
        private List<PropertyListener>     
112
                              transparencyPropertyListener = new ArrayList<PropertyListener>();
113
        /**
114
         * Flag de activaci?n de la transparencia cuando se visualiza o exporta.
115
         */
116
        private boolean       transparencyActive    = false;
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
                switch (originalData.getDataType()) {
267
                case Buffer.TYPE_BYTE:
268
                        if(noData.isDefined() && 
269
                                        originalData.getElemByte(line, col, 0) == noData.getValue().byteValue())
270
                                return true;
271
                        break;
272
                case Buffer.TYPE_SHORT:
273
                        if(noData.isDefined() && 
274
                                        originalData.getElemShort(line, col, 0) == noData.getValue().shortValue())
275
                                return true;
276
                        break;
277
                case Buffer.TYPE_INT:
278
                        if(noData.isDefined() && 
279
                                        originalData.getElemInt(line, col, 0) == noData.getValue().intValue())
280
                                return true;
281
                        break;
282
                case Buffer.TYPE_FLOAT:
283
                        float f = originalData.getElemFloat(line, col, 0);
284
                        if((noData.isDefined() && 
285
                                        f == noData.getValue().floatValue()) || Float.isNaN(f))
286
                                return true;
287
                        break;
288
                case Buffer.TYPE_DOUBLE:
289
                        double d = originalData.getElemDouble(line, col, 0);
290
                        if((noData.isDefined() && 
291
                                        d == noData.getValue().doubleValue()) || Double.isNaN(d))
292
                                return true;
293
                        break;
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
                if (state.hasValue("opacity")) {
422
                        opacity = state.getInt("opacity");
423
                }
424
                
425
                List<TransparencyRange> tr = state.getList("transparencyRange");
426
                if(tr != null) {
427
                        transparencyRanges = new ArrayList<TransparencyRange>(tr);        
428
                        if (state.hasValue("bandnumber")) {
429
                                int alphaBandNumber = state.getInt("bandnumber");
430
                                setTransparencyBand(alphaBandNumber);
431
                        }
432
                }
433

    
434
                this.transparencyActive = state.getBoolean("transparencyActive");
435
                //this.noData = state.getDouble("noData");
436
        }
437

    
438
        public void saveToState(PersistentState state) throws PersistenceException {
439
                if(getOpacity() != 255) {
440
                        state.set("opacity", getOpacity());
441
                }        
442

    
443
                //Rangos de transparencia                
444
                state.set("transparencyRange", transparencyRanges);
445

    
446
                if(getAlphaBandNumber() != -1) {
447
                        state.set("bandnumber", getAlphaBandNumber());
448
                }
449
                
450
                state.set("transparencyActive", transparencyActive);
451
                //state.set("noData", noData);
452
        }
453
        
454
        public static void registerPersistence() {
455
                PersistenceManager manager = ToolsLocator.getPersistenceManager();
456
                DynStruct definition = manager.addDefinition(
457
                                DataStoreTransparency.class,
458
                                PERSISTENCE_NAME,
459
                                "Transparency Persistent definition (FIXME)",
460
                                null, 
461
                                null
462
                );
463
                definition.addDynFieldInt("opacity").setMandatory(false);
464
                definition.addDynFieldInt("bandnumber").setMandatory(false);
465
                definition.addDynFieldList("transparencyRange").setClassOfItems(TransparencyRange.class).setMandatory(false);
466
                definition.addDynFieldBoolean("transparencyActive").setMandatory(false);
467
                //definition.addDynFieldDouble("noData").setMandatory(false);
468
        }
469
        
470
        public void dispose() {
471
                if (originalData != null)
472
                        originalData.dispose();
473
                originalData = null;
474
        }
475
        
476
        public ColorInterpretation getColorInterpretation() {
477
                return colorInterpretation;
478
        }
479
        
480
        protected void finalize() throws Throwable {
481
                noData           = null;
482
                if(transparencyPropertyListener != null) {
483
                        transparencyPropertyListener.clear();
484
                        transparencyPropertyListener = null;
485
                }
486
                if(transparencyRanges != null) {
487
                        transparencyRanges.clear();
488
                        transparencyRanges = null;
489
                }
490
                super.finalize();
491
        }
492
        
493
}