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.imageio.codec; 040 041import java.io.Serializable; 042import java.util.EnumSet; 043 044import org.dcm4che3.conf.core.api.ConfigurableClass; 045import org.dcm4che3.conf.core.api.ConfigurableProperty; 046import org.dcm4che3.conf.core.api.LDAP; 047import org.dcm4che3.image.PhotometricInterpretation; 048import org.dcm4che3.util.Property; 049import org.dcm4che3.util.StringUtils; 050 051/** 052 * @author Gunter Zeilinger <gunterze@gmail.com> 053 * 054 */ 055@LDAP(objectClasses = "dcmCompressionRule") 056@ConfigurableClass 057public class CompressionRule 058 implements Comparable<CompressionRule>, Serializable { 059 060 private static final long serialVersionUID = 2010254518169306864L; 061 062 @ConfigurableProperty(name = "cn") 063 private String commonName; 064 065 @LDAP(noContainerNode = true) 066 @ConfigurableProperty(name = "condition") 067 private Condition condition; 068 069 @ConfigurableProperty(name = "dicomTransferSyntax") 070 private String tsuid; 071 072 @ConfigurableProperty(name = "dcmImageWriteParam") 073 private Property[] imageWriteParams; 074 075 public CompressionRule() { 076 } 077 078 /** 079 * @deprecated Device name is not supported. Use the other constructor. 080 */ 081 @Deprecated 082 public CompressionRule(String commonName, String[] pmis, int[] bitsStored, 083 int pixelRepresentation, String[] aeTitles, String[] deviceNames, 084 String[] sopClasses, String[] imgTypes, String[] bodyPartExamined, 085 String tsuid, String... params) { 086 this(commonName, pmis, bitsStored, pixelRepresentation, aeTitles, sopClasses, imgTypes, bodyPartExamined, tsuid, params); 087 } 088 089 public CompressionRule(String commonName, String[] pmis, int[] bitsStored, 090 int pixelRepresentation, String[] aeTitles, 091 String[] sopClasses, String[] imgTypes, String[] bodyPartExamined, 092 String tsuid, String... params) { 093 this.commonName = commonName; 094 this.condition = new Condition(pmis, bitsStored, pixelRepresentation, 095 StringUtils.maskNull(aeTitles), 096 StringUtils.maskNull(sopClasses), 097 StringUtils.maskNull(imgTypes), 098 StringUtils.maskNull(bodyPartExamined)); 099 this.tsuid = tsuid; 100 this.imageWriteParams = Property.valueOf(params); 101 } 102 103 public void setCommonName(String commonName) { 104 this.commonName = commonName; 105 } 106 107 public Condition getCondition() { 108 return condition; 109 } 110 111 public void setCondition(Condition condition) { 112 this.condition = condition; 113 this.condition.calcWeight(); 114 } 115 116 public String getTsuid() { 117 return tsuid; 118 } 119 120 public void setTsuid(String tsuid) { 121 this.tsuid = tsuid; 122 } 123 124 public void setImageWriteParams(Property[] imageWriteParams) { 125 this.imageWriteParams = imageWriteParams; 126 } 127 128 public final String getCommonName() { 129 return commonName; 130 } 131 132 public PhotometricInterpretation[] getPhotometricInterpretations() { 133 return condition.getPhotometricInterpretations(); 134 } 135 136 public int[] getBitsStored() { 137 return condition.getBitsStored(); 138 } 139 140 public final int getPixelRepresentation() { 141 return condition.pixelRepresentation; 142 } 143 144 public final String[] getAETitles() { 145 return condition.aeTitles; 146 } 147 148 public final String[] getSOPClasses() { 149 return condition.sopClasses; 150 } 151 152 public final String[] getBodyPartExamined() { 153 return condition.bodyPartExamined; 154 } 155 156 public final String getTransferSyntax() { 157 return tsuid; 158 } 159 160 public Property[] getImageWriteParams() { 161 return imageWriteParams; 162 } 163 164 public boolean matchesCondition(PhotometricInterpretation pmi, 165 int bitsStored, int pixelRepresentation, String aeTitle, 166 String sopClass, String[] imgTypes, 167 String bodyPart) { 168 return condition.matches(pmi, bitsStored, pixelRepresentation, aeTitle, 169 sopClass, imgTypes, bodyPart); 170 } 171 172 @Override 173 public int compareTo(CompressionRule o) { 174 return condition.compareTo(o.condition); 175 } 176 177 @ConfigurableClass 178 public static class Condition 179 implements Comparable<Condition>, Serializable { 180 181 private static final long serialVersionUID = -4069284624944470710L; 182 183 @ConfigurableProperty(name = "dcmPhotometricInterpretation") 184 EnumSet<PhotometricInterpretation> pmis; 185 186 /** 187 * Proxy-property, actually stored in bitsStoredMask, see getter/setter 188 */ 189 @ConfigurableProperty(name = "dcmBitsStored") 190 int bitsStoredMaskArray[]; 191 192 int bitsStoredMask; 193 194 @ConfigurableProperty( 195 name = "dcmPixelRepresentation", 196 description = "If equals to -1, ignores pixel representation", 197 defaultValue = "-1") 198 int pixelRepresentation = -1; 199 200 @ConfigurableProperty(name = "dcmAETitle") 201 String[] aeTitles; 202 203 @ConfigurableProperty(name = "dcmSOPClass") 204 String[] sopClasses; 205 206 @ConfigurableProperty(name = "dcmImageType") 207 String[] imageType; 208 209 @ConfigurableProperty(name = "dcmBodyPartExamined") 210 String[] bodyPartExamined; 211 212 int weight; 213 214 public Condition() { 215 } 216 217 Condition(String[] pmis, int[] bitsStored, int pixelRepresentation, 218 String[] aeTitles, String[] sopClasses, 219 String[] imgTypes, String[] bodyPartExamined) { 220 221 this.pmis = EnumSet.noneOf(PhotometricInterpretation.class); 222 for (String pmi : pmis) 223 this.pmis.add(PhotometricInterpretation.fromString(pmi)); 224 225 this.bitsStoredMask = toBitsStoredMask(bitsStored); 226 this.aeTitles = aeTitles; 227 this.sopClasses = sopClasses; 228 this.imageType = imgTypes; 229 this.bodyPartExamined = bodyPartExamined; 230 this.pixelRepresentation = pixelRepresentation; 231 calcWeight(); 232 } 233 234 public void calcWeight() { 235 this.weight = (aeTitles.length != 0 ? 16 : 0) 236 + (sopClasses.length != 0 ? 4 : 0) 237 + (bodyPartExamined.length != 0 ? 2 : 0) 238 + (imageType.length != 0 ? 1 : 0); 239 } 240 241 public EnumSet<PhotometricInterpretation> getPmis() { 242 return pmis; 243 } 244 245 public void setPmis(EnumSet<PhotometricInterpretation> pmis) { 246 this.pmis = pmis; 247 } 248 249 public int getBitsStoredMask() { 250 return bitsStoredMask; 251 } 252 253 public void setBitsStoredMask(int bitsStoredMask) { 254 this.bitsStoredMask = bitsStoredMask; 255 } 256 257 public int getPixelRepresentation() { 258 return pixelRepresentation; 259 } 260 261 public void setPixelRepresentation(int pixelRepresentation) { 262 this.pixelRepresentation = pixelRepresentation; 263 } 264 265 public String[] getAeTitles() { 266 return aeTitles; 267 } 268 269 public void setAeTitles(String[] aeTitles) { 270 this.aeTitles = aeTitles; 271 } 272 273 public String[] getSopClasses() { 274 return sopClasses; 275 } 276 277 public void setSopClasses(String[] sopClasses) { 278 this.sopClasses = sopClasses; 279 } 280 281 public String[] getImageType() { 282 return imageType; 283 } 284 285 public void setImageType(String[] imageType) { 286 this.imageType = imageType; 287 } 288 289 public String[] getBodyPartExamined() { 290 return bodyPartExamined; 291 } 292 293 public void setBodyPartExamined(String[] bodyPartExamined) { 294 this.bodyPartExamined = bodyPartExamined; 295 } 296 297 private int toBitsStoredMask(int[] bitsStored) { 298 int mask = 0; 299 for (int i : bitsStored) 300 mask |= 1 << i; 301 302 return mask; 303 } 304 305 PhotometricInterpretation[] getPhotometricInterpretations() { 306 return pmis.toArray(new PhotometricInterpretation[pmis.size()]); 307 } 308 309 int[] getBitsStored() { 310 int n = 0; 311 for (int i = 8; i <= 16; i++) 312 if (matchBitStored(i)) 313 n++; 314 315 int[] bitsStored = new int[n]; 316 for (int i = 8, j = 0; i <= 16; i++) 317 if (matchBitStored(i)) 318 bitsStored[j++] = i; 319 320 return bitsStored; 321 } 322 323 324 public int[] getBitsStoredMaskArray() { 325 return getBitsStored(); 326 } 327 328 public void setBitsStoredMaskArray(int[] bitsStoredMaskArray) { 329 this.bitsStoredMask = toBitsStoredMask(bitsStoredMaskArray); 330 } 331 332 @Override 333 public int compareTo(Condition o) { 334 return o.weight - weight; 335 } 336 337 public boolean matches(PhotometricInterpretation pmi, 338 int bitsStored, int pixelRepresentation, 339 String aeTitle, 340 String sopClass, String[] imgTypes, String bodyPart) { 341 return pmis.contains(pmi) 342 && matchBitStored(bitsStored) 343 && matchPixelRepresentation(pixelRepresentation) 344 && isEmptyOrContains(this.aeTitles, aeTitle) 345 && isEmptyOrContains(this.sopClasses, sopClass) 346 && isEmptyOrContains(this.imageType, imgTypes) 347 && isEmptyOrContains(this.bodyPartExamined, bodyPart); 348 } 349 350 private boolean matchPixelRepresentation(int pixelRepresentation) { 351 return this.pixelRepresentation == -1 352 || this.pixelRepresentation == pixelRepresentation; 353 } 354 355 private boolean matchBitStored(int bitsStored) { 356 return ((1<<bitsStored) & bitsStoredMask) != 0; 357 } 358 359 private static boolean isEmptyOrContains(Object[] a, Object o) { 360 if (o == null || a.length == 0) 361 return true; 362 363 for (int i = 0; i < a.length; i++) 364 if (o.equals(a[i])) 365 return true; 366 367 return false; 368 } 369 370 private static boolean isEmptyOrContains(Object[] a1, Object[] a2) { 371 if (a1 == null || a1.length == 0 || a2 == null || a2.length == 0) 372 return true; 373 374 for (int i = 0; i < a2.length; i++) 375 if (isEmptyOrContains(a1, a2[i])) 376 return true; 377 378 return false; 379 } 380 } 381 382}