001/*-
002 *******************************************************************************
003 * Copyright (c) 2022 Diamond Light Source Ltd.
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *
009 * Contributors:
010 *    Peter Chang - initial API and implementation and/or initial documentation
011 *******************************************************************************/
012
013package org.eclipse.january.dataset;
014
015import java.util.Arrays;
016
017import org.slf4j.Logger;
018import org.slf4j.LoggerFactory;
019
020/**
021 * Class to hold colour datasets as red, green, blue tuples of byte integers. Note that
022 * the values are treated as unsigned integers
023 * @since 2.3
024 */
025public class RGBByteDataset extends CompoundByteDataset implements Cloneable {
026        // pin UID to base class
027        private static final long serialVersionUID = Dataset.serialVersionUID;
028
029        private static final Logger logger = LoggerFactory.getLogger(RGBByteDataset.class);
030
031        private static final int ISIZE = 3; // number of elements per item
032
033        /**
034         * Create a null dataset
035         */
036        public RGBByteDataset() {
037                super(ISIZE);
038        }
039
040        /**
041         * @param shape output shape
042         */
043        public RGBByteDataset(final int... shape) {
044                super(ISIZE, shape);
045        }
046
047        /**
048         * @param data interleaved RGB values
049         * @param shape output shape
050         */
051        public RGBByteDataset(final byte[] data, final int... shape) {
052                super(ISIZE, data, shape);
053        }
054
055        /**
056         * Copy a dataset
057         * @param dataset to clone
058         */
059        public RGBByteDataset(final RGBByteDataset dataset) {
060                super(dataset);
061        }
062
063        @Override
064        public RGBByteDataset clone() {
065                return new RGBByteDataset(this);
066        }
067
068        /**
069         * Create a dataset using given data (red, green and blue parts are given separately)
070         * @param redData data for red
071         * @param greenData data for green
072         * @param blueData data for blue
073         * @param shape (can be null to create 1D dataset)
074         */
075        public RGBByteDataset(final int[] redData, final int[] greenData, final int[] blueData, int... shape) {
076                int dsize = redData.length > greenData.length ? greenData.length : redData.length;
077                dsize = dsize > blueData.length ? blueData.length : dsize;
078                if (shape == null || shape.length == 0) {
079                        shape = new int[] {dsize};
080                }
081                isize = ISIZE;
082                size = ShapeUtils.calcSize(shape);
083                if (size != dsize) {
084                        logger.error("Shape is not compatible with size of data array");
085                        throw new IllegalArgumentException("Shape is not compatible with size of data array");
086                }
087                this.shape = shape.clone();
088
089                try {
090                        odata = data = createArray(size);
091                } catch (Throwable t) {
092                        logger.error("Could not create a dataset of shape {}", Arrays.toString(shape), t);
093                        throw new IllegalArgumentException(t);
094                }
095
096                for (int i = 0, n = 0; i < size; i++) {
097                        data[n++] = (byte) redData[i];
098                        data[n++] = (byte) greenData[i];
099                        data[n++] = (byte) blueData[i];
100                }
101        }
102
103        /**
104         * Create a dataset using given data (red, green and blue parts are given separately)
105         * @param redData data for red
106         * @param greenData data for green
107         * @param blueData data for blue
108         * @param shape (can be null to create 1D dataset)
109         */
110        public RGBByteDataset(final short[] redData, final short[] greenData, final short[] blueData, int... shape) {
111                int dsize = redData.length > greenData.length ? greenData.length : redData.length;
112                dsize = dsize > blueData.length ? blueData.length : dsize;
113                if (shape == null || shape.length == 0) {
114                        shape = new int[] {dsize};
115                }
116                isize = ISIZE;
117                size = ShapeUtils.calcSize(shape);
118                if (size != dsize) {
119                        logger.error("Shape is not compatible with size of data array");
120                        throw new IllegalArgumentException("Shape is not compatible with size of data array");
121                }
122                this.shape = shape.clone();
123
124                try {
125                        odata = data = createArray(size);
126                } catch (Throwable t) {
127                        logger.error("Could not create a dataset of shape {}", Arrays.toString(shape), t);
128                        throw new IllegalArgumentException(t);
129                }
130
131                for (int i = 0, n = 0; i < size; i++) {
132                        data[n++] = (byte) redData[i];
133                        data[n++] = (byte) greenData[i];
134                        data[n++] = (byte) blueData[i];
135                }
136        }
137
138        /**
139         * Create a dataset using given data (red, green and blue parts are given separately)
140         * @param redData data for red
141         * @param greenData data for green
142         * @param blueData data for blue
143         * @param shape (can be null to create 1D dataset)
144         */
145        public RGBByteDataset(final byte[] redData, final byte[] greenData, final byte[] blueData, int... shape) {
146                int dsize = redData.length > greenData.length ? greenData.length : redData.length;
147                dsize = dsize > blueData.length ? blueData.length : dsize;
148                if (shape == null || shape.length == 0) {
149                        shape = new int[] {dsize};
150                }
151                isize = ISIZE;
152                size = ShapeUtils.calcSize(shape);
153                if (size != dsize) {
154                        logger.error("Shape is not compatible with size of data array");
155                        throw new IllegalArgumentException("Shape is not compatible with size of data array");
156                }
157                this.shape = shape.clone();
158
159                try {
160                        odata = data = createArray(size);
161                } catch (Throwable t) {
162                        logger.error("Could not create a dataset of shape {}", Arrays.toString(shape), t);
163                        throw new IllegalArgumentException(t);
164                }
165
166                for (int i = 0, n = 0; i < size; i++) {
167                        data[n++] = redData[i];
168                        data[n++] = greenData[i];
169                        data[n++] = blueData[i];
170                }
171        }
172
173        private final static int MIN_VALUE = 0;
174        private final static int MAX_VALUE = 255;
175
176        /**
177         * Create a dataset using given colour data (colour components are given separately)
178         * @param red dataset
179         * @param green dataset
180         * @param blue dataset
181         */
182        public RGBByteDataset(final Dataset red, final Dataset green, final Dataset blue) {
183                super(ISIZE, red.getShapeRef());
184                red.checkCompatibility(green);
185                red.checkCompatibility(blue);
186
187                if (red.max().doubleValue() > MAX_VALUE || red.min().doubleValue() < MIN_VALUE ||
188                                green.max().doubleValue() > MAX_VALUE || green.min().doubleValue() < MIN_VALUE || 
189                                blue.max().doubleValue() > MAX_VALUE || blue.min().doubleValue() < MIN_VALUE) {
190                        logger.warn("Some values are out of range and will be truncated");
191                }
192
193                IndexIterator riter = red.getIterator();
194                IndexIterator giter = green.getIterator();
195                IndexIterator biter = blue.getIterator();
196
197                for (int i = 0; riter.hasNext() && giter.hasNext() && biter.hasNext();) {
198                        data[i++] = (byte) red.getElementLongAbs(riter.index);
199                        data[i++] = (byte) green.getElementLongAbs(giter.index);
200                        data[i++] = (byte) blue.getElementLongAbs(biter.index);
201                }
202        }
203
204        /**
205         * Create a dataset using given grey data
206         * @param grey dataset
207         */
208        public RGBByteDataset(final Dataset grey) {
209                super(ISIZE, grey.getShapeRef());
210
211                IndexIterator giter = grey.getIterator();
212
213                for (int i = 0; giter.hasNext();) {
214                        final byte g = (byte) grey.getElementLongAbs(giter.index); 
215                        data[i++] = g;
216                        data[i++] = g;
217                        data[i++] = g;
218                }
219        }
220
221        /**
222         * Create a dataset using given compound data
223         * @param colour dataset with colour data
224         */
225        public RGBByteDataset(final CompoundDataset colour) {
226                super(ISIZE, colour.getShapeRef());
227
228                if (colour.getElementsPerItem() != 3) {
229                        throw new IllegalArgumentException("Compound dataset must have three elements per item");
230                }
231
232                final IndexIterator it = colour.getIterator();
233                for (int i = 0; it.hasNext();) {
234                        data[i++] = (byte) colour.getElementLongAbs(it.index);
235                        data[i++] = (byte) colour.getElementLongAbs(it.index + 1);
236                        data[i++] = (byte) colour.getElementLongAbs(it.index + 2);
237                }
238        }
239
240        /**
241         * Create a RGB dataset from an object which could be a Java list, array (of arrays...) or Number. Ragged
242         * sequences or arrays are padded with zeros. The item size is the last dimension of the corresponding
243         * elemental dataset
244         *
245         * @param obj object
246         * @return dataset with contents given by input
247         */
248        public static RGBByteDataset createFromObject(final Object obj) {
249                CompoundByteDataset result = (CompoundByteDataset) DatasetUtils.createCompoundDataset(ByteDataset.createFromObject(obj), ISIZE);
250                return new RGBByteDataset(result.data, result.shape);
251        }
252
253        /**
254         * Create a RGB dataset from a compound dataset (no normalisation performed)
255         * @param a dataset
256         * @return RGB dataset (grey if input dataset has less than 3 elements per item)
257         */
258        public static RGBByteDataset createFromCompoundDataset(final CompoundDataset a) {
259                if (a instanceof RGBByteDataset)
260                        return (RGBByteDataset) a;
261                final int is = a.getElementsPerItem();
262                if (is < 3) {
263                        return new RGBByteDataset((Dataset) a);
264                }
265
266                if (a instanceof CompoundByteDataset && is == 3) {
267                        return new RGBByteDataset((byte[]) a.getBuffer(), a.getShapeRef());
268                }
269
270                final RGBByteDataset rgb = new RGBByteDataset(a.getShapeRef());
271                final IndexIterator it = a.getIterator();
272
273                int n = 0;
274                while (it.hasNext()) {
275                        rgb.data[n++] = (byte) a.getElementLongAbs(it.index);
276                        rgb.data[n++] = (byte) a.getElementLongAbs(it.index + 1);
277                        rgb.data[n++] = (byte) a.getElementLongAbs(it.index + 2);
278                }
279
280                return rgb;
281        }
282
283        /**
284         * Create a RGB dataset from hue, saturation and value dataset
285         * @param hue (in degrees from -360 to 360)
286         * @param saturation (from 0 to 1), can be null to denote 1
287         * @param value (from 0 to 1)
288         * @return RGB dataset
289         */
290        public static RGBByteDataset createFromHSV(final Dataset hue, final Dataset saturation, final Dataset value) {
291                if ((saturation != null && !hue.isCompatibleWith(saturation)) || !hue.isCompatibleWith(value)) {
292                        throw new IllegalArgumentException("Hue, saturation and value datasets must have the same shape");
293                }
294
295                RGBByteDataset result = new RGBByteDataset(hue.getShapeRef());
296                IndexIterator it = result.getIterator(true);
297                int[] pos = it.getPos();
298                byte[] rgb = new byte[3];
299
300                if (saturation == null) {
301                        while (it.hasNext()) {
302                                convertHSVToRGB(hue.getDouble(pos), 1, value.getDouble(pos), rgb);
303                                result.setAbs(it.index, rgb);
304                        }
305                } else {
306                        while (it.hasNext()) {
307                                convertHSVToRGB(hue.getDouble(pos), saturation.getDouble(pos), value.getDouble(pos), rgb);
308                                result.setAbs(it.index, rgb);
309                        }
310                }
311
312                return result;
313        }
314
315        /**
316         * Create a RGB dataset from hue, saturation and lightness dataset
317         * @param hue (in degrees from -360 to 360)
318         * @param saturation (from 0 to 1), can be null to denote 1
319         * @param lightness (from 0 to 1)
320         * @return RGB dataset
321         */
322        public static RGBByteDataset createFromHSL(final Dataset hue, final Dataset saturation, final Dataset lightness) {
323                if ((saturation != null && !hue.isCompatibleWith(saturation)) || !hue.isCompatibleWith(lightness)) {
324                        throw new IllegalArgumentException("Hue, saturation and lightness datasets must have the same shape");
325                }
326
327                RGBByteDataset result = new RGBByteDataset(hue.getShapeRef());
328                IndexIterator it = result.getIterator(true);
329                int[] pos = it.getPos();
330                byte[] rgb = new byte[3];
331
332                if (saturation == null) {
333                        while (it.hasNext()) {
334                                convertHSLToRGB(hue.getDouble(pos), 1, lightness.getDouble(pos), rgb);
335                                result.setAbs(it.index, rgb);
336                        }
337                } else {
338                        while (it.hasNext()) {
339                                convertHSLToRGB(hue.getDouble(pos), saturation.getDouble(pos), lightness.getDouble(pos), rgb);
340                                result.setAbs(it.index, rgb);
341                        }
342                }
343
344                return result;
345        }
346
347        private static void convertHSVToRGB(double h, double s, double v, byte[] rgb) {
348                double m = 255 * v;
349                double chroma = s * m;
350                m -= chroma;
351                double hprime = h / 60.;
352                if (hprime < 0) {
353                        hprime += 6;
354                }
355                byte sx = (byte) (chroma * (1 - Math.abs((hprime % 2) - 1)) + m);
356                byte sc = (byte) (chroma + m);
357                byte sm = (byte) m;
358                
359                if (hprime < 1) {
360                        rgb[0] = sc;
361                        rgb[1] = sx;
362                        rgb[2] = sm;
363                } else if (hprime < 2) {
364                        rgb[0] = sx;
365                        rgb[1] = sc;
366                        rgb[2] = sm;
367                } else if (hprime < 3) {
368                        rgb[0] = sm;
369                        rgb[1] = sc;
370                        rgb[2] = sx;
371                } else if (hprime < 4) {
372                        rgb[0] = sm;
373                        rgb[1] = sx;
374                        rgb[2] = sc;
375                } else if (hprime < 5) {
376                        rgb[0] = sx;
377                        rgb[1] = sm;
378                        rgb[2] = sc;
379                } else if (hprime < 6) {
380                        rgb[0] = sc;
381                        rgb[1] = sm;
382                        rgb[2] = sx;
383                } else { // if hue is outside domain
384                        rgb[0] = sm;
385                        rgb[1] = sm;
386                        rgb[2] = sm;
387                }
388        }
389
390        private static void convertHSLToRGB(double h, double s, double l, byte[] rgb) {
391                double m = l;
392                double chroma = s * (1 - Math.abs(2 * m - 1));
393                m -= chroma * 0.5;
394                m *= 255;
395                chroma *= 255;
396                double hprime = h / 60.;
397                if (hprime < 0) {
398                        hprime += 6;
399                }
400                byte sx = (byte) (chroma * (1 - Math.abs((hprime % 2) - 1)) + m);
401                byte sc = (byte) (chroma + m);
402                byte sm = (byte) m;
403                
404                if (hprime < 1) {
405                        rgb[0] = sc;
406                        rgb[1] = sx;
407                        rgb[2] = sm;
408                } else if (hprime < 2) {
409                        rgb[0] = sx;
410                        rgb[1] = sc;
411                        rgb[2] = sm;
412                } else if (hprime < 3) {
413                        rgb[0] = sm;
414                        rgb[1] = sc;
415                        rgb[2] = sx;
416                } else if (hprime < 4) {
417                        rgb[0] = sm;
418                        rgb[1] = sx;
419                        rgb[2] = sc;
420                } else if (hprime < 5) {
421                        rgb[0] = sx;
422                        rgb[1] = sm;
423                        rgb[2] = sc;
424                } else if (hprime < 6) {
425                        rgb[0] = sc;
426                        rgb[1] = sm;
427                        rgb[2] = sx;
428                } else { // if hue is outside domain
429                        rgb[0] = sm;
430                        rgb[1] = sm;
431                        rgb[2] = sm;
432                }
433        }
434
435        @Override
436        public RGBByteDataset getSlice(SliceIterator siter) {
437                CompoundByteDataset base = super.getSlice(siter);
438
439                RGBByteDataset slice = new RGBByteDataset();
440                copyToView(base, slice, false, false);
441                slice.setData();
442                return slice;
443        }
444
445        @Override
446        public RGBByteDataset getView(boolean deepCopyMetadata) {
447                RGBByteDataset view = new RGBByteDataset();
448                copyToView(this, view, true, deepCopyMetadata);
449                view.setData();
450                return view;
451        }
452
453        /**
454         * @return red value in the first position
455         */
456        public byte getRed() {
457                return data[getFirst1DIndex()];
458        }
459
460        /**
461         * @param i position in first dimension
462         * @return red value in given position
463         */
464        public byte getRed(final int i) {
465                return data[get1DIndex(i)];
466        }
467
468        /**
469         * @param i position in first dimension
470         * @param j position in second dimension
471         * @return red value in given position
472         */
473        public byte getRed(final int i, final int j) {
474                return data[get1DIndex(i, j)];
475        }
476
477        /**
478         * @param pos position
479         * @return red value in given position
480         */
481        public byte getRed(final int... pos) {
482                return data[get1DIndex(pos)];
483        }
484
485        /**
486         * @return green value in the first position
487         */
488        public byte getGreen() {
489                return data[getFirst1DIndex() + 1];
490        }
491
492        /**
493         * @param i position in first dimension
494         * @return green value in given position
495         */
496        public byte getGreen(final int i) {
497                return data[get1DIndex(i) + 1];
498        }
499
500        /**
501         * @param i position in first dimension
502         * @param j position in second dimension
503         * @return green value in given position
504         */
505        public byte getGreen(final int i, final int j) {
506                return data[get1DIndex(i, j) + 1];
507        }
508
509        /**
510         * @param pos position
511         * @return green value in given position
512         */
513        public byte getGreen(final int... pos) {
514                return data[get1DIndex(pos) + 1];
515        }
516
517        /**
518         * @return blue value in the first position
519         */
520        public byte getBlue() {
521                return data[getFirst1DIndex() + 2];
522        }
523
524        /**
525         * @param i position in first dimension
526         * @return blue value in given position
527         */
528        public byte getBlue(final int i) {
529                return data[get1DIndex(i) + 2];
530        }
531
532        /**
533         * @param i position in first dimension
534         * @param j position in second dimension
535         * @return blue value in given position
536         */
537        public byte getBlue(final int i, final int j) {
538                return data[get1DIndex(i, j) + 2];
539        }
540
541        /**
542         * @param pos position
543         * @return blue value in given position
544         */
545        public byte getBlue(final int... pos) {
546                return data[get1DIndex(pos) + 2];
547        }
548
549        /**
550         * Get a red value from given absolute index as a byte - note this index does not
551         * take in account the item size so be careful when using with multi-element items
552         * 
553         * @param n absolute index
554         * @return red value
555         */
556        public byte getRedAbs(int n) {
557                return data[n*isize];
558        }
559
560        /**
561         * Get a green value from given absolute index as a byte - note this index does not
562         * take in account the item size so be careful when using with multi-element items
563         * 
564         * @param n absolute index
565         * @return green value
566         */
567        public byte getGreenAbs(int n) {
568                return data[n*isize + 1];
569        }
570
571        /**
572         * Get a blue value from given absolute index as a byte - note this index does not
573         * take in account the item size so be careful when using with multi-element items
574         * 
575         * @param n absolute index
576         * @return blue value
577         */
578        public byte getBlueAbs(int n) {
579                return data[n*isize + 2];
580        }
581
582
583        // weights from NTSC formula aka ITU-R BT.601 for mapping RGB to luma
584        private static final double Wr = 0.299, Wg = 0.587, Wb = 0.114;
585
586        /**
587         * Convert colour dataset to a grey-scale one using the NTSC formula, aka ITU-R BT.601, for RGB to luma mapping
588         * @param <T> dataset sub-interface
589         * @param clazz dataset sub-interface
590         * @return a grey-scale dataset of given type
591         */
592        public <T extends Dataset> T createGreyDataset(final Class<T> clazz) {
593                return createGreyDataset(clazz, Wr, Wg, Wb);
594        }
595
596        /**
597         * Convert colour dataset to a grey-scale one using given RGB to luma mapping
598         * @param <T> dataset sub-interface
599         * @param clazz dataset sub-interface
600         * @param red weight
601         * @param green weight
602         * @param blue weight
603         * @return a grey-scale dataset of given class
604         */
605        public <T extends Dataset> T createGreyDataset(final Class<T> clazz, final double red, final double green, final double blue) {
606                final T grey = DatasetFactory.zeros(clazz, shape);
607                final IndexIterator it = getIterator();
608
609                int i = 0;
610                while (it.hasNext()) {
611                        grey.setObjectAbs(i++, red*Byte.toUnsignedInt(data[it.index]) + green*Byte.toUnsignedInt(data[it.index + 1]) + blue*Byte.toUnsignedInt(data[it.index + 2]));
612                }
613                return grey;
614        }
615
616        /**
617         * Convert colour dataset to a grey-scale one using the NTSC formula, aka ITU-R BT.601, for RGB to luma mapping
618         * @param dtype dataset type
619         * @return a grey-scale dataset of given class
620         * @deprecated Use {@link RGBByteDataset#createGreyDataset(Class)}
621         */
622        @Deprecated
623        public Dataset createGreyDataset(final int dtype) {
624                return createGreyDataset(Wr, Wg, Wb, dtype);
625        }
626
627        /**
628         * Convert colour dataset to a grey-scale one using given RGB to luma mapping
629         * @param red weight
630         * @param green weight
631         * @param blue weight
632         * @param dtype dataset type
633         * @return a grey-scale dataset of given type
634         * @deprecated Use {@link RGBByteDataset#createGreyDataset(Class, double, double, double)}
635         */
636        @Deprecated
637        public Dataset createGreyDataset(final double red, final double green, final double blue, final int dtype) {
638                return createGreyDataset(DTypeUtils.getInterface(dtype), red, green, blue);
639        }
640
641        /**
642         * Extract red colour channel
643         * @param <T> dataset sub-interface
644         * @param clazz dataset sub-interface
645         * @return a dataset of given class
646         */
647        public <T extends Dataset> T createRedDataset(final Class<T> clazz) {
648                return createColourChannelDataset(0, clazz, "red");
649        }
650
651        /**
652         * Extract green colour channel
653         * @param <T> dataset sub-interface
654         * @param clazz dataset sub-interface
655         * @return a dataset of given class
656         */
657        public <T extends Dataset> T createGreenDataset(final Class<T> clazz) {
658                return createColourChannelDataset(1, clazz, "green");
659        }
660
661        /**
662         * Extract blue colour channel
663         * @param <T> dataset sub-interface
664         * @param clazz dataset sub-interface
665         * @return a dataset of given class
666         */
667        public <T extends Dataset> T createBlueDataset(final Class<T> clazz) {
668                return createColourChannelDataset(2, clazz, "blue");
669        }
670
671        /**
672         * Extract red colour channel
673         * @param dtype dataset type
674         * @return a dataset of given type
675         * @deprecated Use {@link #createRedDataset}
676         */
677        @Deprecated
678        public Dataset createRedDataset(final int dtype) {
679                return createColourChannelDataset(0, DTypeUtils.getInterface(dtype), "red");
680        }
681
682        /**
683         * Extract green colour channel
684         * @param dtype dataset type
685         * @return a dataset of given type
686         * @deprecated Use {@link #createGreenDataset}
687         */
688        @Deprecated
689        public Dataset createGreenDataset(final int dtype) {
690                return createColourChannelDataset(1, DTypeUtils.getInterface(dtype), "green");
691        }
692
693        /**
694         * Extract blue colour channel
695         * @param dtype dataset type
696         * @return a dataset of given type
697         * @deprecated Use {@link #createBlueDataset}
698         */
699        @Deprecated
700        public Dataset createBlueDataset(final int dtype) {
701                return createColourChannelDataset(2, DTypeUtils.getInterface(dtype), "blue");
702        }
703
704        private <T extends Dataset> T createColourChannelDataset(int channelOffset, Class<T> clazz, String cName) {
705                final T channel = DatasetFactory.zeros(clazz, shape);
706
707                final StringBuilder cname = name == null ? new StringBuilder() : new StringBuilder(name);
708                if (cname.length() > 0) {
709                        cname.append('.');
710                }
711                cname.append(cName);
712                channel.setName(cname.toString());
713
714                final IndexIterator it = getIterator();
715
716                int i = 0;
717                while (it.hasNext()) {
718                        channel.setObjectAbs(i++, Byte.toUnsignedInt(data[it.index + channelOffset]));
719                }
720
721                return channel;
722        }
723
724
725        /**
726         * @return red view
727         */
728        public ByteDataset getRedView() {
729                return getColourChannelView(0, "red");
730        }
731
732        /**
733         * @return green view
734         */
735        public ByteDataset getGreenView() {
736                return getColourChannelView(1, "green");
737        }
738
739        /**
740         * @return blue view
741         */
742        public ByteDataset getBlueView() {
743                return getColourChannelView(2, "blue");
744        }
745
746        private ByteDataset getColourChannelView(final int channelOffset, final String cName) {
747                ByteDataset view = getElements(channelOffset);
748                view.setName(cName);
749                return view;
750        }
751
752        @Override
753        public double getElementDoubleAbs(final int index) {
754                return Byte.toUnsignedInt(data[index]);
755        }
756
757        @Override
758        public long getElementLongAbs(final int index) {
759                return Byte.toUnsignedInt(data[index]);
760        }
761
762        @Override
763        public Number max(boolean... ignored) {
764                int max = 0;
765                final IndexIterator it = getIterator();
766
767                while (it.hasNext()) {
768                        for (int i = 0; i < ISIZE; i++) {
769                                int value = Byte.toUnsignedInt(data[it.index + i]);
770                                if (value > max)
771                                        max = value;
772                        }
773                }
774                return max;
775        }
776
777        @Override
778        public Number min(boolean... ignored) {
779                int min = MAX_VALUE;
780                final IndexIterator it = getIterator();
781
782                while (it.hasNext()) {
783                        for (int i = 0; i < ISIZE; i++) {
784                                int value = Byte.toUnsignedInt(data[it.index + i]);
785                                if (value < min)
786                                        min = value;
787                        }
788                }
789                return min;
790        }
791}