001/*******************************************************************************
002 * This software is provided as a supplement to the authors' textbooks on digital
003 * image processing published by Springer-Verlag in various languages and editions.
004 * Permission to use and distribute this software is granted under the BSD 2-Clause
005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause).
006 * Copyright (c) 2006-2023 Wilhelm Burger, Mark J. Burge. All rights reserved.
007 * Visit https://imagingbook.com for additional details.
008 ******************************************************************************/
009package imagingbook.common.image.access;
010
011import ij.process.ColorProcessor;
012import imagingbook.common.image.OutOfBoundsStrategy;
013import imagingbook.common.image.interpolation.InterpolationMethod;
014
015/**
016 * A special vector-valued image accessor for RGB images (direct subclass of {@link VectorAccessor}) with depth = 3
017 * color components.
018 *
019 * @author WB
020 * @version 2022/09/22
021 */
022public class RgbAccessor extends VectorAccessor {
023        
024        private final int[] pixels;
025
026        /**
027         * Constructor.
028         * @param ip the associated image
029         * @param obs the out-of-bounds strategy to be used
030         * @param ipm the interpolation method to be used
031         */
032        public RgbAccessor(ColorProcessor ip, OutOfBoundsStrategy obs, InterpolationMethod ipm) {
033                super(ip, 3, obs, ipm);
034                this.pixels = (int[]) this.ip.getPixels();
035        }
036        
037        @Override
038        ScalarAccessor makeComponentAccessor(int k) {
039                return new ComponentAccessor((ColorProcessor)ip, outOfBoundsStrategy, interpolationMethod, k);
040        }
041        
042        public static RgbAccessor create(ColorProcessor ip, OutOfBoundsStrategy obs, InterpolationMethod ipm) {
043                return new RgbAccessor(ip, obs, ipm);
044        }
045        
046        // ---------------------------------------------------------------------
047
048        @Override
049        public float[] getPix(int u, int v) { // returns an RGB value packed into a float[]
050                float red = componentAccessors[0].getVal(u, v);  //(c & 0xff0000) >> 16;
051                float grn = componentAccessors[1].getVal(u, v);  //(c & 0xff00) >> 8;
052                float blu = componentAccessors[2].getVal(u, v);  //(c & 0xff);
053                return new float[] { red, grn, blu };
054        }
055        
056        @Override
057        public float[] getPix(double x, double y) {
058                float red = componentAccessors[0].getVal(x, y);
059                float grn = componentAccessors[1].getVal(x, y);
060                float blu = componentAccessors[2].getVal(x, y); 
061                return new float[] { red, grn, blu };
062        }
063        
064        @Override
065        public void setPix(int u, int v, float[] valf) {
066                for (int k = 0; k < 3; k++) {
067                        componentAccessors[k].setVal(u, v, valf[k]);
068                }
069        }
070        
071        @Override
072        public ColorProcessor getProcessor() {
073                return (ColorProcessor) this.ip;
074        }
075        
076        // ----------------------------------------------------------------
077        
078        private interface ComponentGetter {
079                /**
080                 * Function to extract a particular RGB component from an integer.
081                 * @param rgb a packed RGB integer
082                 * @return a component value
083                 */
084                int get(int rgb); 
085        }
086        
087        private interface ComponentSetter {
088                /**
089                 * Function to insert a particular component value into an integer.
090                 * @param rgb a packed RGB integer
091                 * @param c the component value to insert
092                 * @return the modified RGB integer
093                 */
094                int set(int rgb, int c);
095        }
096
097        /**
098         * Defines a scalar image accessor for one particular color component. This is a non-static inner class, which
099         * shares data with the enclosing {@link RgbAccessor} instance. Note that this subclass of {@link ScalarAccessor}
100         * has no ImageProcessor of is own, but {@link #getProcessor()} returns the original {@link ColorProcessor}
101         * instance.
102         */
103        private class ComponentAccessor extends ScalarAccessor {
104                
105                private final int k;    // the RGB component index = 0,1,2
106                        
107                private final ComponentGetter[] getComponent = {                        // note the fancy use of lambda expressions!
108                                (rgb) -> (rgb & 0xff0000) >> 16,        // extract red component
109                                (rgb) -> (rgb & 0x00ff00) >> 8,         // extract green component
110                                (rgb) -> (rgb & 0x0000ff) };            // extract blue component
111                
112                private final ComponentSetter[] setComponent = {
113                                (rgb, r) -> (rgb & 0xff00ffff) | (r & 0xff) << 16,      // insert red component
114                                (rgb, g) -> (rgb & 0xffff00ff) | (g & 0xff) << 8,       // insert green component
115                                (rgb, b) -> (rgb & 0xffffff00) | (b & 0xff)                     // insert blue component
116                };
117                
118                private ComponentAccessor(ColorProcessor ip, OutOfBoundsStrategy obs, InterpolationMethod ipm, int k) {
119                        super(ip, obs, ipm);
120                        this.k = k;
121                }
122                
123
124                @Override
125                public float getVal(int u, int v) {
126                        int i = indexer.getIndex(u, v);
127                        if (i < 0) {
128                                return this.defaultValue;
129                        } else {
130                                return getComponent[k].get(pixels[i]);
131                        }
132                }
133
134                @Override
135                public void setVal(int u, int v, float val) {
136                        int i = indexer.getIndex(u, v);
137                        if (i >= 0) {
138                                int vali = clamp(Math.round(val));
139                                pixels[i] = setComponent[k].set(pixels[i], vali);
140                        }
141                }
142                
143                private int clamp(int val) {
144                        if (val < 0) return 0;
145                        if (val > 255) return 255;
146                        return val;
147                }
148        }
149
150}