Source Code Cross Referenced for RescaleOp.java in  » 6.0-JDK-Core » AWT » java » awt » image » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Home
Java Source Code / Java Documentation
1.6.0 JDK Core
2.6.0 JDK Modules
3.6.0 JDK Modules com.sun
4.6.0 JDK Modules com.sun.java
5.6.0 JDK Modules sun
6.6.0 JDK Platform
7.Ajax
8.Apache Harmony Java SE
9.Aspect oriented
10.Authentication Authorization
11.Blogger System
12.Build
13.Byte Code
14.Cache
15.Chart
16.Chat
17.Code Analyzer
18.Collaboration
19.Content Management System
20.Database Client
21.Database DBMS
22.Database JDBC Connection Pool
23.Database ORM
24.Development
25.EJB Server
26.ERP CRM Financial
27.ESB
28.Forum
29.Game
30.GIS
31.Graphic 3D
32.Graphic Library
33.Groupware
34.HTML Parser
35.IDE
36.IDE Eclipse
37.IDE Netbeans
38.Installer
39.Internationalization Localization
40.Inversion of Control
41.Issue Tracking
42.J2EE
43.J2ME
44.JBoss
45.JMS
46.JMX
47.Library
48.Mail Clients
49.Music
50.Net
51.Parser
52.PDF
53.Portal
54.Profiler
55.Project Management
56.Report
57.RSS RDF
58.Rule Engine
59.Science
60.Scripting
61.Search Engine
62.Security
63.Sevlet Container
64.Source Control
65.Swing Library
66.Template Engine
67.Test Coverage
68.Testing
69.UML
70.Web Crawler
71.Web Framework
72.Web Mail
73.Web Server
74.Web Services
75.Web Services apache cxf 2.2.6
76.Web Services AXIS2
77.Wiki Engine
78.Workflow Engines
79.XML
80.XML UI
Java Source Code / Java Documentation » 6.0 JDK Core » AWT » java.awt.image 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001        /*
002         * Copyright 1997-2000 Sun Microsystems, Inc.  All Rights Reserved.
003         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004         *
005         * This code is free software; you can redistribute it and/or modify it
006         * under the terms of the GNU General Public License version 2 only, as
007         * published by the Free Software Foundation.  Sun designates this
008         * particular file as subject to the "Classpath" exception as provided
009         * by Sun in the LICENSE file that accompanied this code.
010         *
011         * This code is distributed in the hope that it will be useful, but WITHOUT
012         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013         * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014         * version 2 for more details (a copy is included in the LICENSE file that
015         * accompanied this code).
016         *
017         * You should have received a copy of the GNU General Public License version
018         * 2 along with this work; if not, write to the Free Software Foundation,
019         * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020         *
021         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022         * CA 95054 USA or visit www.sun.com if you need additional information or
023         * have any questions.
024         */
025
026        package java.awt.image;
027
028        import java.awt.color.ColorSpace;
029        import java.awt.geom.Rectangle2D;
030        import java.awt.Rectangle;
031        import java.awt.geom.Point2D;
032        import java.awt.RenderingHints;
033        import sun.awt.image.ImagingLib;
034
035        /**
036         * This class performs a pixel-by-pixel rescaling of the data in the
037         * source image by multiplying the sample values for each pixel by a scale
038         * factor and then adding an offset. The scaled sample values are clipped
039         * to the minimum/maximum representable in the destination image.
040         * <p>
041         * The pseudo code for the rescaling operation is as follows:
042         * <pre>
043         *for each pixel from Source object {
044         *    for each band/component of the pixel {
045         *        dstElement = (srcElement*scaleFactor) + offset
046         *    }
047         *}
048         * </pre>
049         * <p>
050         * For Rasters, rescaling operates on bands.  The number of
051         * sets of scaling constants may be one, in which case the same constants
052         * are applied to all bands, or it must equal the number of Source
053         * Raster bands.
054         * <p>
055         * For BufferedImages, rescaling operates on color and alpha components.
056         * The number of sets of scaling constants may be one, in which case the
057         * same constants are applied to all color (but not alpha) components.
058         * Otherwise, the  number of sets of scaling constants may
059         * equal the number of Source color components, in which case no
060         * rescaling of the alpha component (if present) is performed.
061         * If neither of these cases apply, the number of sets of scaling constants
062         * must equal the number of Source color components plus alpha components,
063         * in which case all color and alpha components are rescaled.
064         * <p>
065         * BufferedImage sources with premultiplied alpha data are treated in the same
066         * manner as non-premultiplied images for purposes of rescaling.  That is,
067         * the rescaling is done per band on the raw data of the BufferedImage source
068         * without regard to whether the data is premultiplied.  If a color conversion
069         * is required to the destination ColorModel, the premultiplied state of
070         * both source and destination will be taken into account for this step.
071         * <p>
072         * Images with an IndexColorModel cannot be rescaled.
073         * <p>
074         * If a RenderingHints object is specified in the constructor, the
075         * color rendering hint and the dithering hint may be used when color
076         * conversion is required.
077         * <p>
078         * Note that in-place operation is allowed (i.e. the source and destination can
079         * be the same object).
080         * @version 10 Feb 1997
081         * @see java.awt.RenderingHints#KEY_COLOR_RENDERING
082         * @see java.awt.RenderingHints#KEY_DITHERING
083         */
084        public class RescaleOp implements  BufferedImageOp, RasterOp {
085            float[] scaleFactors;
086            float[] offsets;
087            int length = 0;
088            RenderingHints hints;
089
090            private int srcNbits;
091            private int dstNbits;
092
093            /**
094             * Constructs a new RescaleOp with the desired scale factors
095             * and offsets.  The length of the scaleFactor and offset arrays
096             * must meet the restrictions stated in the class comments above.
097             * The RenderingHints argument may be null.
098             * @param scaleFactors the specified scale factors
099             * @param offsets the specified offsets
100             * @param hints the specified <code>RenderingHints</code>, or
101             *        <code>null</code>
102             */
103            public RescaleOp(float[] scaleFactors, float[] offsets,
104                    RenderingHints hints) {
105                length = scaleFactors.length;
106                if (length > offsets.length)
107                    length = offsets.length;
108
109                this .scaleFactors = new float[length];
110                this .offsets = new float[length];
111                for (int i = 0; i < length; i++) {
112                    this .scaleFactors[i] = scaleFactors[i];
113                    this .offsets[i] = offsets[i];
114                }
115                this .hints = hints;
116            }
117
118            /**
119             * Constructs a new RescaleOp with the desired scale factor
120             * and offset.  The scaleFactor and offset will be applied to
121             * all bands in a source Raster and to all color (but not alpha)
122             * components in a BufferedImage.
123             * The RenderingHints argument may be null.
124             * @param scaleFactor the specified scale factor
125             * @param offset the specified offset
126             * @param hints the specified <code>RenderingHints</code>, or
127             *        <code>null</code>
128             */
129            public RescaleOp(float scaleFactor, float offset,
130                    RenderingHints hints) {
131                length = 1;
132                this .scaleFactors = new float[1];
133                this .offsets = new float[1];
134                this .scaleFactors[0] = scaleFactor;
135                this .offsets[0] = offset;
136                this .hints = hints;
137            }
138
139            /** 
140             * Returns the scale factors in the given array. The array is also
141             * returned for convenience.  If scaleFactors is null, a new array
142             * will be allocated.
143             * @param scaleFactors the array to contain the scale factors of 
144             *        this <code>RescaleOp</code>
145             * @return the scale factors of this <code>RescaleOp</code>.
146             */
147            final public float[] getScaleFactors(float scaleFactors[]) {
148                if (scaleFactors == null) {
149                    return (float[]) this .scaleFactors.clone();
150                }
151                System.arraycopy(this .scaleFactors, 0, scaleFactors, 0, Math
152                        .min(this .scaleFactors.length, scaleFactors.length));
153                return scaleFactors;
154            }
155
156            /**
157             * Returns the offsets in the given array. The array is also returned
158             * for convenience.  If offsets is null, a new array
159             * will be allocated.
160             * @param offsets the array to contain the offsets of 
161             *        this <code>RescaleOp</code>
162             * @return the offsets of this <code>RescaleOp</code>.
163             */
164            final public float[] getOffsets(float offsets[]) {
165                if (offsets == null) {
166                    return (float[]) this .offsets.clone();
167                }
168
169                System.arraycopy(this .offsets, 0, offsets, 0, Math.min(
170                        this .offsets.length, offsets.length));
171                return offsets;
172            }
173
174            /**
175             * Returns the number of scaling factors and offsets used in this
176             * RescaleOp.
177             * @return the number of scaling factors and offsets of this 
178             *         <code>RescaleOp</code>.
179             */
180            final public int getNumFactors() {
181                return length;
182            }
183
184            /**
185             * Creates a ByteLookupTable to implement the rescale.
186             * The table may have either a SHORT or BYTE input.
187             * @param nElems    Number of elements the table is to have.
188             *                  This will generally be 256 for byte and
189             *                  65536 for short.
190             */
191            private ByteLookupTable createByteLut(float scale[], float off[],
192                    int nBands, int nElems) {
193
194                byte[][] lutData = new byte[scale.length][nElems];
195
196                for (int band = 0; band < scale.length; band++) {
197                    float bandScale = scale[band];
198                    float bandOff = off[band];
199                    byte[] bandLutData = lutData[band];
200                    for (int i = 0; i < nElems; i++) {
201                        int val = (int) (i * bandScale + bandOff);
202                        if ((val & 0xffffff00) != 0) {
203                            if (val < 0) {
204                                val = 0;
205                            } else {
206                                val = 255;
207                            }
208                        }
209                        bandLutData[i] = (byte) val;
210                    }
211
212                }
213
214                return new ByteLookupTable(0, lutData);
215            }
216
217            /**
218             * Creates a ShortLookupTable to implement the rescale.
219             * The table may have either a SHORT or BYTE input.
220             * @param nElems    Number of elements the table is to have.
221             *                  This will generally be 256 for byte and
222             *                  65536 for short.
223             */
224            private ShortLookupTable createShortLut(float scale[], float off[],
225                    int nBands, int nElems) {
226
227                short[][] lutData = new short[scale.length][nElems];
228
229                for (int band = 0; band < scale.length; band++) {
230                    float bandScale = scale[band];
231                    float bandOff = off[band];
232                    short[] bandLutData = lutData[band];
233                    for (int i = 0; i < nElems; i++) {
234                        int val = (int) (i * bandScale + bandOff);
235                        if ((val & 0xffff0000) != 0) {
236                            if (val < 0) {
237                                val = 0;
238                            } else {
239                                val = 65535;
240                            }
241                        }
242                        bandLutData[i] = (short) val;
243                    }
244                }
245
246                return new ShortLookupTable(0, lutData);
247            }
248
249            /**
250             * Determines if the rescale can be performed as a lookup.
251             * The dst must be a byte or short type.
252             * The src must be less than 16 bits.
253             * All source band sizes must be the same and all dst band sizes
254             * must be the same.
255             */
256            private boolean canUseLookup(Raster src, Raster dst) {
257
258                //
259                // Check that the src datatype is either a BYTE or SHORT
260                //
261                int datatype = src.getDataBuffer().getDataType();
262                if (datatype != DataBuffer.TYPE_BYTE
263                        && datatype != DataBuffer.TYPE_USHORT) {
264                    return false;
265                }
266
267                //
268                // Check dst sample sizes. All must be 8 or 16 bits.
269                //
270                SampleModel dstSM = dst.getSampleModel();
271                dstNbits = dstSM.getSampleSize(0);
272
273                if (!(dstNbits == 8 || dstNbits == 16)) {
274                    return false;
275                }
276                for (int i = 1; i < src.getNumBands(); i++) {
277                    int bandSize = dstSM.getSampleSize(i);
278                    if (bandSize != dstNbits) {
279                        return false;
280                    }
281                }
282
283                //
284                // Check src sample sizes. All must be the same size 
285                //
286                SampleModel srcSM = src.getSampleModel();
287                srcNbits = srcSM.getSampleSize(0);
288                if (srcNbits > 16) {
289                    return false;
290                }
291                for (int i = 1; i < src.getNumBands(); i++) {
292                    int bandSize = srcSM.getSampleSize(i);
293                    if (bandSize != srcNbits) {
294                        return false;
295                    }
296                }
297
298                return true;
299            }
300
301            /**
302             * Rescales the source BufferedImage.  
303             * If the color model in the source image is not the same as that
304             * in the destination image, the pixels will be converted
305             * in the destination.  If the destination image is null,
306             * a BufferedImage will be created with the source ColorModel.
307             * An IllegalArgumentException may be thrown if the number of
308             * scaling factors/offsets in this object does not meet the
309             * restrictions stated in the class comments above, or if the
310             * source image has an IndexColorModel.
311             * @param src the <code>BufferedImage</code> to be filtered
312             * @param dst the destination for the filtering operation 
313             *            or <code>null</code>
314             * @return the filtered <code>BufferedImage</code>.
315             * @throws IllegalArgumentException if the <code>ColorModel</code>
316             *         of <code>src</code> is an <code>IndexColorModel</code>,  
317             *         or if the number of scaling factors and offsets in this
318             *         <code>RescaleOp</code> do not meet the requirements 
319             *         stated in the class comments.
320             */
321            public final BufferedImage filter(BufferedImage src,
322                    BufferedImage dst) {
323                ColorModel srcCM = src.getColorModel();
324                ColorModel dstCM;
325                int numBands = srcCM.getNumColorComponents();
326
327                if (srcCM instanceof  IndexColorModel) {
328                    throw new IllegalArgumentException("Rescaling cannot be "
329                            + "performed on an indexed image");
330                }
331                if (length != 1 && length != numBands
332                        && length != srcCM.getNumComponents()) {
333                    throw new IllegalArgumentException(
334                            "Number of scaling constants "
335                                    + "does not equal the number of"
336                                    + " of color or color/alpha "
337                                    + " components");
338                }
339
340                boolean needToConvert = false;
341
342                // Include alpha
343                if (length > numBands && srcCM.hasAlpha()) {
344                    length = numBands + 1;
345                }
346
347                int width = src.getWidth();
348                int height = src.getHeight();
349
350                if (dst == null) {
351                    dst = createCompatibleDestImage(src, null);
352                    dstCM = srcCM;
353                } else {
354                    if (width != dst.getWidth()) {
355                        throw new IllegalArgumentException("Src width ("
356                                + width + ") not equal to dst width ("
357                                + dst.getWidth() + ")");
358                    }
359                    if (height != dst.getHeight()) {
360                        throw new IllegalArgumentException("Src height ("
361                                + height + ") not equal to dst height ("
362                                + dst.getHeight() + ")");
363                    }
364
365                    dstCM = dst.getColorModel();
366                    if (srcCM.getColorSpace().getType() != dstCM
367                            .getColorSpace().getType()) {
368                        needToConvert = true;
369                        dst = createCompatibleDestImage(src, null);
370                    }
371
372                }
373
374                BufferedImage origDst = dst;
375
376                //
377                // Try to use a native BI rescale operation first
378                //
379                if (ImagingLib.filter(this , src, dst) == null) {
380                    //
381                    // Native BI rescale failed - convert to rasters
382                    //
383                    WritableRaster srcRaster = src.getRaster();
384                    WritableRaster dstRaster = dst.getRaster();
385
386                    if (srcCM.hasAlpha()) {
387                        if (numBands - 1 == length || length == 1) {
388                            int minx = srcRaster.getMinX();
389                            int miny = srcRaster.getMinY();
390                            int[] bands = new int[numBands - 1];
391                            for (int i = 0; i < numBands - 1; i++) {
392                                bands[i] = i;
393                            }
394                            srcRaster = srcRaster.createWritableChild(minx,
395                                    miny, srcRaster.getWidth(), srcRaster
396                                            .getHeight(), minx, miny, bands);
397                        }
398                    }
399                    if (dstCM.hasAlpha()) {
400                        int dstNumBands = dstRaster.getNumBands();
401                        if (dstNumBands - 1 == length || length == 1) {
402                            int minx = dstRaster.getMinX();
403                            int miny = dstRaster.getMinY();
404                            int[] bands = new int[numBands - 1];
405                            for (int i = 0; i < numBands - 1; i++) {
406                                bands[i] = i;
407                            }
408                            dstRaster = dstRaster.createWritableChild(minx,
409                                    miny, dstRaster.getWidth(), dstRaster
410                                            .getHeight(), minx, miny, bands);
411                        }
412                    }
413
414                    //
415                    // Call the raster filter method
416                    //
417                    filter(srcRaster, dstRaster);
418
419                }
420
421                if (needToConvert) {
422                    // ColorModels are not the same
423                    ColorConvertOp ccop = new ColorConvertOp(hints);
424                    ccop.filter(dst, origDst);
425                }
426
427                return origDst;
428            }
429
430            /**
431             * Rescales the pixel data in the source Raster.
432             * If the destination Raster is null, a new Raster will be created.
433             * The source and destination must have the same number of bands.
434             * Otherwise, an IllegalArgumentException is thrown.
435             * Note that the number of scaling factors/offsets in this object must
436             * meet the restrictions stated in the class comments above.
437             * Otherwise, an IllegalArgumentException is thrown.
438             * @param src the <code>Raster</code> to be filtered
439             * @param dst the destination for the filtering operation 
440             *            or <code>null</code>
441             * @return the filtered <code>WritableRaster</code>.
442             * @throws IllegalArgumentException if <code>src</code> and
443             *         <code>dst</code> do not have the same number of bands,  
444             *         or if the number of scaling factors and offsets in this
445             *         <code>RescaleOp</code> do not meet the requirements 
446             *         stated in the class comments.
447             */
448            public final WritableRaster filter(Raster src, WritableRaster dst) {
449                int numBands = src.getNumBands();
450                int width = src.getWidth();
451                int height = src.getHeight();
452                int[] srcPix = null;
453                int step = 0;
454                int tidx = 0;
455
456                // Create a new destination Raster, if needed
457                if (dst == null) {
458                    dst = createCompatibleDestRaster(src);
459                } else if (height != dst.getHeight() || width != dst.getWidth()) {
460                    throw new IllegalArgumentException(
461                            "Width or height of Rasters do not " + "match");
462                } else if (numBands != dst.getNumBands()) {
463                    // Make sure that the number of bands are equal
464                    throw new IllegalArgumentException(
465                            "Number of bands in src "
466                                    + numBands
467                                    + " does not equal number of bands in dest "
468                                    + dst.getNumBands());
469                }
470                // Make sure that the arrays match
471                // Make sure that the low/high/constant arrays match
472                if (length != 1 && length != src.getNumBands()) {
473                    throw new IllegalArgumentException(
474                            "Number of scaling constants "
475                                    + "does not equal the number of"
476                                    + " of bands in the src raster");
477                }
478
479                //
480                // Try for a native raster rescale first
481                //
482                if (ImagingLib.filter(this , src, dst) != null) {
483                    return dst;
484                }
485
486                //
487                // Native raster rescale failed.
488                // Try to see if a lookup operation can be used
489                //
490                if (canUseLookup(src, dst)) {
491                    int srcNgray = (1 << srcNbits);
492                    int dstNgray = (1 << dstNbits);
493
494                    if (dstNgray == 256) {
495                        ByteLookupTable lut = createByteLut(scaleFactors,
496                                offsets, numBands, srcNgray);
497                        LookupOp op = new LookupOp(lut, hints);
498                        op.filter(src, dst);
499                    } else {
500                        ShortLookupTable lut = createShortLut(scaleFactors,
501                                offsets, numBands, srcNgray);
502                        LookupOp op = new LookupOp(lut, hints);
503                        op.filter(src, dst);
504                    }
505                } else {
506                    //
507                    // Fall back to the slow code
508                    //
509                    if (length > 1) {
510                        step = 1;
511                    }
512
513                    int sminX = src.getMinX();
514                    int sY = src.getMinY();
515                    int dminX = dst.getMinX();
516                    int dY = dst.getMinY();
517                    int sX;
518                    int dX;
519
520                    //
521                    //  Determine bits per band to determine maxval for clamps.
522                    //  The min is assumed to be zero. 
523                    //  REMIND: This must change if we ever support signed data types.
524                    //
525                    int nbits;
526                    int dstMax[] = new int[numBands];
527                    int dstMask[] = new int[numBands];
528                    SampleModel dstSM = dst.getSampleModel();
529                    for (int z = 0; z < numBands; z++) {
530                        nbits = dstSM.getSampleSize(z);
531                        dstMax[z] = (1 << nbits) - 1;
532                        dstMask[z] = ~(dstMax[z]);
533                    }
534
535                    int val;
536                    for (int y = 0; y < height; y++, sY++, dY++) {
537                        dX = dminX;
538                        sX = sminX;
539                        for (int x = 0; x < width; x++, sX++, dX++) {
540                            // Get data for all bands at this x,y position
541                            srcPix = src.getPixel(sX, sY, srcPix);
542                            tidx = 0;
543                            for (int z = 0; z < numBands; z++, tidx += step) {
544                                val = (int) (srcPix[z] * scaleFactors[tidx] + offsets[tidx]);
545                                // Clamp
546                                if ((val & dstMask[z]) != 0) {
547                                    if (val < 0) {
548                                        val = 0;
549                                    } else {
550                                        val = dstMax[z];
551                                    }
552                                }
553                                srcPix[z] = val;
554
555                            }
556
557                            // Put it back for all bands
558                            dst.setPixel(dX, dY, srcPix);
559                        }
560                    }
561                }
562                return dst;
563            }
564
565            /**
566             * Returns the bounding box of the rescaled destination image.  Since
567             * this is not a geometric operation, the bounding box does not
568             * change.
569             */
570            public final Rectangle2D getBounds2D(BufferedImage src) {
571                return getBounds2D(src.getRaster());
572            }
573
574            /**
575             * Returns the bounding box of the rescaled destination Raster.  Since
576             * this is not a geometric operation, the bounding box does not
577             * change.
578             * @param src the rescaled destination <code>Raster</code>
579             * @return the bounds of the specified <code>Raster</code>.
580             */
581            public final Rectangle2D getBounds2D(Raster src) {
582                return src.getBounds();
583            }
584
585            /**
586             * Creates a zeroed destination image with the correct size and number of
587             * bands.
588             * @param src       Source image for the filter operation.
589             * @param destCM    ColorModel of the destination.  If null, the
590             *                  ColorModel of the source will be used.
591             * @return the zeroed-destination image.
592             */
593            public BufferedImage createCompatibleDestImage(BufferedImage src,
594                    ColorModel destCM) {
595                BufferedImage image;
596                if (destCM == null) {
597                    ColorModel cm = src.getColorModel();
598                    image = new BufferedImage(cm, src.getRaster()
599                            .createCompatibleWritableRaster(), cm
600                            .isAlphaPremultiplied(), null);
601                } else {
602                    int w = src.getWidth();
603                    int h = src.getHeight();
604                    image = new BufferedImage(destCM, destCM
605                            .createCompatibleWritableRaster(w, h), destCM
606                            .isAlphaPremultiplied(), null);
607                }
608
609                return image;
610            }
611
612            /**
613             * Creates a zeroed-destination <code>Raster</code> with the correct 
614             * size and number of bands, given this source.
615             * @param src       the source <code>Raster</code>
616             * @return the zeroed-destination <code>Raster</code>.
617             */
618            public WritableRaster createCompatibleDestRaster(Raster src) {
619                return src.createCompatibleWritableRaster(src.getWidth(), src
620                        .getHeight());
621            }
622
623            /**
624             * Returns the location of the destination point given a
625             * point in the source.  If dstPt is non-null, it will
626             * be used to hold the return value.  Since this is not a geometric
627             * operation, the srcPt will equal the dstPt.
628             * @param srcPt a point in the source image
629             * @param dstPt the destination point or <code>null</code>
630             * @return the location of the destination point.
631             */
632            public final Point2D getPoint2D(Point2D srcPt, Point2D dstPt) {
633                if (dstPt == null) {
634                    dstPt = new Point2D.Float();
635                }
636                dstPt.setLocation(srcPt.getX(), srcPt.getY());
637                return dstPt;
638            }
639
640            /**
641             * Returns the rendering hints for this op.
642             * @return the rendering hints of this <code>RescaleOp</code>.
643             */
644            public final RenderingHints getRenderingHints() {
645                return hints;
646            }
647        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.