/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is part of dcm4che, an implementation of DICOM(TM) in * Java(TM), hosted at https://github.com/gunterze/dcm4che. * * The Initial Developer of the Original Code is * Agfa Healthcare. * Portions created by the Initial Developer are Copyright (C) 2013 * the Initial Developer. All Rights Reserved. * * Contributor(s): * See @authors listed below * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ package org.dcm4che3.imageio.codec; import java.io.Serializable; import java.util.EnumSet; import org.dcm4che3.conf.core.api.ConfigurableClass; import org.dcm4che3.conf.core.api.ConfigurableProperty; import org.dcm4che3.conf.core.api.LDAP; import org.dcm4che3.image.PhotometricInterpretation; import org.dcm4che3.util.Property; import org.dcm4che3.util.StringUtils; /** * @author Gunter Zeilinger <gunterze@gmail.com> * */ @LDAP(objectClasses = "dcmCompressionRule") @ConfigurableClass public class CompressionRule implements Comparable<CompressionRule>, Serializable { private static final long serialVersionUID = 2010254518169306864L; @ConfigurableProperty(name = "cn") private String commonName; @LDAP(noContainerNode = true) @ConfigurableProperty(name = "condition") private Condition condition; @ConfigurableProperty(name = "dicomTransferSyntax") private String tsuid; @ConfigurableProperty(name = "dcmImageWriteParam") private Property[] imageWriteParams; public CompressionRule() { } /** * @deprecated Device name is not supported. Use the other constructor. */ @Deprecated public CompressionRule(String commonName, String[] pmis, int[] bitsStored, int pixelRepresentation, String[] aeTitles, String[] deviceNames, String[] sopClasses, String[] imgTypes, String[] bodyPartExamined, String tsuid, String... params) { this(commonName, pmis, bitsStored, pixelRepresentation, aeTitles, sopClasses, imgTypes, bodyPartExamined, tsuid, params); } public CompressionRule(String commonName, String[] pmis, int[] bitsStored, int pixelRepresentation, String[] aeTitles, String[] sopClasses, String[] imgTypes, String[] bodyPartExamined, String tsuid, String... params) { this.commonName = commonName; this.condition = new Condition(pmis, bitsStored, pixelRepresentation, StringUtils.maskNull(aeTitles), StringUtils.maskNull(sopClasses), StringUtils.maskNull(imgTypes), StringUtils.maskNull(bodyPartExamined)); this.tsuid = tsuid; this.imageWriteParams = Property.valueOf(params); } public void setCommonName(String commonName) { this.commonName = commonName; } public Condition getCondition() { return condition; } public void setCondition(Condition condition) { this.condition = condition; this.condition.calcWeight(); } public String getTsuid() { return tsuid; } public void setTsuid(String tsuid) { this.tsuid = tsuid; } public void setImageWriteParams(Property[] imageWriteParams) { this.imageWriteParams = imageWriteParams; } public final String getCommonName() { return commonName; } public PhotometricInterpretation[] getPhotometricInterpretations() { return condition.getPhotometricInterpretations(); } public int[] getBitsStored() { return condition.getBitsStored(); } public final int getPixelRepresentation() { return condition.pixelRepresentation; } public final String[] getAETitles() { return condition.aeTitles; } public final String[] getSOPClasses() { return condition.sopClasses; } public final String[] getBodyPartExamined() { return condition.bodyPartExamined; } public final String getTransferSyntax() { return tsuid; } public Property[] getImageWriteParams() { return imageWriteParams; } public boolean matchesCondition(PhotometricInterpretation pmi, int bitsStored, int pixelRepresentation, String aeTitle, String sopClass, String[] imgTypes, String bodyPart) { return condition.matches(pmi, bitsStored, pixelRepresentation, aeTitle, sopClass, imgTypes, bodyPart); } @Override public int compareTo(CompressionRule o) { return condition.compareTo(o.condition); } @ConfigurableClass public static class Condition implements Comparable<Condition>, Serializable { private static final long serialVersionUID = -4069284624944470710L; @ConfigurableProperty(name = "dcmPhotometricInterpretation") EnumSet<PhotometricInterpretation> pmis; /** * Proxy-property, actually stored in bitsStoredMask, see getter/setter */ @ConfigurableProperty(name = "dcmBitsStored") int bitsStoredMaskArray[]; int bitsStoredMask; @ConfigurableProperty( name = "dcmPixelRepresentation", description = "If equals to -1, ignores pixel representation", defaultValue = "-1") int pixelRepresentation = -1; @ConfigurableProperty(name = "dcmAETitle") String[] aeTitles; @ConfigurableProperty(name = "dcmSOPClass") String[] sopClasses; @ConfigurableProperty(name = "dcmImageType") String[] imageType; @ConfigurableProperty(name = "dcmBodyPartExamined") String[] bodyPartExamined; int weight; public Condition() { } Condition(String[] pmis, int[] bitsStored, int pixelRepresentation, String[] aeTitles, String[] sopClasses, String[] imgTypes, String[] bodyPartExamined) { this.pmis = EnumSet.noneOf(PhotometricInterpretation.class); for (String pmi : pmis) this.pmis.add(PhotometricInterpretation.fromString(pmi)); this.bitsStoredMask = toBitsStoredMask(bitsStored); this.aeTitles = aeTitles; this.sopClasses = sopClasses; this.imageType = imgTypes; this.bodyPartExamined = bodyPartExamined; this.pixelRepresentation = pixelRepresentation; calcWeight(); } public void calcWeight() { this.weight = (aeTitles.length != 0 ? 16 : 0) + (sopClasses.length != 0 ? 4 : 0) + (bodyPartExamined.length != 0 ? 2 : 0) + (imageType.length != 0 ? 1 : 0); } public EnumSet<PhotometricInterpretation> getPmis() { return pmis; } public void setPmis(EnumSet<PhotometricInterpretation> pmis) { this.pmis = pmis; } public int getBitsStoredMask() { return bitsStoredMask; } public void setBitsStoredMask(int bitsStoredMask) { this.bitsStoredMask = bitsStoredMask; } public int getPixelRepresentation() { return pixelRepresentation; } public void setPixelRepresentation(int pixelRepresentation) { this.pixelRepresentation = pixelRepresentation; } public String[] getAeTitles() { return aeTitles; } public void setAeTitles(String[] aeTitles) { this.aeTitles = aeTitles; } public String[] getSopClasses() { return sopClasses; } public void setSopClasses(String[] sopClasses) { this.sopClasses = sopClasses; } public String[] getImageType() { return imageType; } public void setImageType(String[] imageType) { this.imageType = imageType; } public String[] getBodyPartExamined() { return bodyPartExamined; } public void setBodyPartExamined(String[] bodyPartExamined) { this.bodyPartExamined = bodyPartExamined; } private int toBitsStoredMask(int[] bitsStored) { int mask = 0; for (int i : bitsStored) mask |= 1 << i; return mask; } PhotometricInterpretation[] getPhotometricInterpretations() { return pmis.toArray(new PhotometricInterpretation[pmis.size()]); } int[] getBitsStored() { int n = 0; for (int i = 8; i <= 16; i++) if (matchBitStored(i)) n++; int[] bitsStored = new int[n]; for (int i = 8, j = 0; i <= 16; i++) if (matchBitStored(i)) bitsStored[j++] = i; return bitsStored; } public int[] getBitsStoredMaskArray() { return getBitsStored(); } public void setBitsStoredMaskArray(int[] bitsStoredMaskArray) { this.bitsStoredMask = toBitsStoredMask(bitsStoredMaskArray); } @Override public int compareTo(Condition o) { return o.weight - weight; } public boolean matches(PhotometricInterpretation pmi, int bitsStored, int pixelRepresentation, String aeTitle, String sopClass, String[] imgTypes, String bodyPart) { return pmis.contains(pmi) && matchBitStored(bitsStored) && matchPixelRepresentation(pixelRepresentation) && isEmptyOrContains(this.aeTitles, aeTitle) && isEmptyOrContains(this.sopClasses, sopClass) && isEmptyOrContains(this.imageType, imgTypes) && isEmptyOrContains(this.bodyPartExamined, bodyPart); } private boolean matchPixelRepresentation(int pixelRepresentation) { return this.pixelRepresentation == -1 || this.pixelRepresentation == pixelRepresentation; } private boolean matchBitStored(int bitsStored) { return ((1<<bitsStored) & bitsStoredMask) != 0; } private static boolean isEmptyOrContains(Object[] a, Object o) { if (o == null || a.length == 0) return true; for (int i = 0; i < a.length; i++) if (o.equals(a[i])) return true; return false; } private static boolean isEmptyOrContains(Object[] a1, Object[] a2) { if (a1 == null || a1.length == 0 || a2 == null || a2.length == 0) return true; for (int i = 0; i < a2.length; i++) if (isEmptyOrContains(a1, a2[i])) return true; return false; } } }