001/* ***** BEGIN LICENSE BLOCK *****
002 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
003 *
004 * The contents of this file are subject to the Mozilla Public License Version
005 * 1.1 (the "License"); you may not use this file except in compliance with
006 * the License. You may obtain a copy of the License at
007 * http://www.mozilla.org/MPL/
008 *
009 * Software distributed under the License is distributed on an "AS IS" basis,
010 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
011 * for the specific language governing rights and limitations under the
012 * License.
013 *
014 * The Original Code is part of dcm4che, an implementation of DICOM(TM) in
015 * Java(TM), hosted at https://github.com/gunterze/dcm4che.
016 *
017 * The Initial Developer of the Original Code is
018 * Agfa Healthcare.
019 * Portions created by the Initial Developer are Copyright (C) 2013
020 * the Initial Developer. All Rights Reserved.
021 *
022 * Contributor(s):
023 * See @authors listed below
024 *
025 * Alternatively, the contents of this file may be used under the terms of
026 * either the GNU General Public License Version 2 or later (the "GPL"), or
027 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
028 * in which case the provisions of the GPL or the LGPL are applicable instead
029 * of those above. If you wish to allow use of your version of this file only
030 * under the terms of either the GPL or the LGPL, and not to allow others to
031 * use your version of this file under the terms of the MPL, indicate your
032 * decision by deleting the provisions above and replace them with the notice
033 * and other provisions required by the GPL or the LGPL. If you do not delete
034 * the provisions above, a recipient may use your version of this file under
035 * the terms of any one of the MPL, the GPL or the LGPL.
036 *
037 * ***** END LICENSE BLOCK ***** */
038
039package org.dcm4che3.image;
040
041import java.awt.image.ComponentSampleModel;
042import java.awt.image.DataBuffer;
043import java.awt.image.DataBufferByte;
044import java.awt.image.DataBufferShort;
045import java.awt.image.DataBufferUShort;
046import java.awt.image.Raster;
047import java.awt.image.WritableRaster;
048import java.util.Arrays;
049
050import org.dcm4che3.data.Tag;
051import org.dcm4che3.data.Attributes;
052import org.dcm4che3.data.Sequence;
053import org.dcm4che3.util.TagUtils;
054
055/**
056 * @author Gunter Zeilinger <gunterze@gmail.com>
057 *
058 */
059public class Overlays {
060
061    public static int[] getActiveOverlayGroupOffsets(Attributes psattrs) {
062        return getOverlayGroupOffsets(psattrs, Tag.OverlayActivationLayer, -1);
063    }
064
065    public static int[] getActiveOverlayGroupOffsets(Attributes attrs,
066            int activationMask) {
067        return getOverlayGroupOffsets(attrs, Tag.OverlayRows, activationMask);
068    }
069
070    public static int[] getOverlayGroupOffsets(Attributes attrs, int tag,
071            int activationMask) {
072        int len = 0;
073        int[] result = new int[16];
074        for (int i = 0; i < result.length; i++) {
075            int gg0000 = i << 17;
076            if ((activationMask & (1<<i)) != 0
077                    &&  attrs.containsValue(tag | gg0000))
078                result[len++] = gg0000;
079        }
080        return Arrays.copyOf(result, len);
081    }
082
083    public static int[] getEmbeddedOverlayGroupOffsets(Attributes attrs) {
084        int len = 0;
085        int[] result = new int[16];
086        for (int i = 0; i < result.length; i++) {
087            int gg0000 = i << 17;
088            if (attrs.getInt(Tag.OverlayBitsAllocated | gg0000, 1) != 1)
089                result[len++] = gg0000;
090        }
091        return Arrays.copyOf(result, len);
092    }
093
094   public static void extractFromPixeldata(Raster raster, int mask, 
095            byte[] ovlyData, int off, int length) {
096        ComponentSampleModel sm = (ComponentSampleModel) raster.getSampleModel();
097        int rows = raster.getHeight();
098        int columns = raster.getWidth();
099        int stride = sm.getScanlineStride();
100        DataBuffer db = raster.getDataBuffer();
101        switch (db.getDataType()) {
102        case DataBuffer.TYPE_BYTE:
103            extractFromPixeldata(((DataBufferByte) db).getData(),
104                    rows, columns, stride, mask,
105                    ovlyData, off, length);
106            break;
107        case DataBuffer.TYPE_USHORT:
108            extractFromPixeldata(((DataBufferUShort) db).getData(),
109                    rows, columns, stride, mask,
110                    ovlyData, off, length);
111            break;
112        case DataBuffer.TYPE_SHORT:
113            extractFromPixeldata(((DataBufferShort) db).getData(),
114                    rows, columns, stride, mask,
115                    ovlyData, off, length);
116            break;
117        default:
118            throw new UnsupportedOperationException(
119                    "Unsupported DataBuffer type: " + db.getDataType());
120        }
121    }
122
123    private static void extractFromPixeldata(byte[] pixeldata,
124            int rows, int columns, int stride, int mask,
125            byte[] ovlyData, int off, int length) {
126        for (int y = 0, i = off, imax = off + length;
127                y < columns && i < imax; y++) {
128            for (int j = y * stride, jmax = j + rows; j < jmax && i < imax; j++, i++) {
129                if ((pixeldata[j] & mask) != 0)
130                    ovlyData[i >>> 3] |= 1 << (i & 7);
131            }
132        }
133    }
134
135    private static void extractFromPixeldata(short[] pixeldata,
136            int rows, int columns, int stride, int mask,
137            byte[] ovlyData, int off, int length) {
138        for (int y = 0, i = off, imax = off + length;
139                y < rows && i < imax; y++) {
140            for (int j = y * stride, jmax = j + columns; j < jmax && i < imax; j++, i++) {
141                if ((pixeldata[j] & mask) != 0) {
142                    ovlyData[i >>> 3] |= 1 << (i & 7);
143                }
144            }
145        }
146    }
147
148    public static int getRecommendedDisplayGrayscaleValue(Attributes psAttrs,
149            int gg0000) {
150        int tagOverlayActivationLayer = Tag.OverlayActivationLayer | gg0000;
151        String layerName = psAttrs.getString(tagOverlayActivationLayer);
152        if (layerName == null)
153            throw new IllegalArgumentException("Missing "
154                    + TagUtils.toString(tagOverlayActivationLayer)
155                    + " Overlay Activation Layer");
156        Sequence layers = psAttrs.getSequence(Tag.GraphicLayerSequence);
157        if (layers == null)
158            throw new IllegalArgumentException("Missing "
159                    + TagUtils.toString(Tag.GraphicLayerSequence)
160                    + " Graphic Layer Sequence");
161        
162        for (Attributes layer : layers)
163            if (layerName.equals(layer.getString(Tag.GraphicLayer)))
164                return layer.getInt(Tag.RecommendedDisplayGrayscaleValue, -1);
165
166        throw new IllegalArgumentException("No Graphic Layer: " + layerName);
167    }
168
169    public static void applyOverlay(int frameIndex, WritableRaster raster,
170            Attributes attrs, int gg0000, int pixelValue, byte[] ovlyData) {
171
172        int imageFrameOrigin = attrs.getInt(Tag.ImageFrameOrigin | gg0000, 1);
173        int framesInOverlay = attrs.getInt(Tag.NumberOfFramesInOverlay | gg0000, 1);
174        int ovlyFrameIndex = frameIndex - imageFrameOrigin  + 1;
175        if (ovlyFrameIndex < 0 || ovlyFrameIndex >= framesInOverlay)
176            return;
177        
178        int tagOverlayRows = Tag.OverlayRows | gg0000;
179        int tagOverlayColumns = Tag.OverlayColumns | gg0000;
180        int tagOverlayData = Tag.OverlayData | gg0000;
181        int tagOverlayOrigin = Tag.OverlayOrigin | gg0000;
182
183        int ovlyRows = attrs.getInt(tagOverlayRows, -1);
184        int ovlyColumns = attrs.getInt(tagOverlayColumns, -1);
185        int[] ovlyOrigin = attrs.getInts(tagOverlayOrigin);
186        if (ovlyData == null)
187            ovlyData = attrs.getSafeBytes(tagOverlayData);
188
189        if (ovlyData == null)
190            throw new IllegalArgumentException("Missing "
191                    + TagUtils.toString(tagOverlayData)
192                    + " Overlay Data");
193        if (ovlyRows <= 0)
194            throw new IllegalArgumentException(
195                    TagUtils.toString(tagOverlayRows)
196                    + " Overlay Rows [" + ovlyRows + "]");
197        if (ovlyColumns <= 0)
198            throw new IllegalArgumentException(
199                    TagUtils.toString(tagOverlayColumns)
200                    + " Overlay Columns [" + ovlyColumns + "]");
201        if (ovlyOrigin == null)
202            throw new IllegalArgumentException("Missing "
203                    + TagUtils.toString(tagOverlayOrigin)
204                    + " Overlay Origin");
205        if (ovlyOrigin.length != 2)
206            throw new IllegalArgumentException(
207                    TagUtils.toString(tagOverlayOrigin)
208                    + " Overlay Origin " + Arrays.toString(ovlyOrigin));
209
210        int x0 = ovlyOrigin[1] - 1;
211        int y0 = ovlyOrigin[0] - 1;
212
213        int ovlyLen = ovlyRows * ovlyColumns;
214        int ovlyOff = ovlyLen * ovlyFrameIndex;
215        for (int i = ovlyOff >>> 3,
216               end = (ovlyOff + ovlyLen + 7) >>> 3; i < end; i++) {
217            int ovlyBits = ovlyData[i] & 0xff;
218            for (int j = 0; (ovlyBits>>>j) != 0; j++) {
219                if ((ovlyBits & (1<<j)) == 0)
220                    continue;
221
222                int ovlyIndex = ((i<<3) + j) - ovlyOff;
223                if (ovlyIndex >= ovlyLen)
224                    continue;
225
226                int y = y0 + ovlyIndex / ovlyColumns;
227                int x = x0 + ovlyIndex % ovlyColumns;
228                try {
229                    raster.setSample(x, y, 0, pixelValue);
230                } catch (ArrayIndexOutOfBoundsException ignore) {}
231            }
232        }
233    }
234
235}