001: /*
002: * Copyright (c) 2007, intarsys consulting GmbH
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * - Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * - Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * - Neither the name of intarsys nor the names of its contributors may be used
015: * to endorse or promote products derived from this software without specific
016: * prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
028: * POSSIBILITY OF SUCH DAMAGE.
029: */
030: package de.intarsys.pdf.pd;
031:
032: import de.intarsys.pdf.cos.COSArray;
033: import de.intarsys.pdf.cos.COSDictionary;
034: import de.intarsys.pdf.cos.COSName;
035: import de.intarsys.pdf.cos.COSObject;
036: import de.intarsys.pdf.cos.COSStream;
037:
038: /**
039: * Function implementation based on samples.
040: */
041: public class PDSampledFunction extends PDFunction {
042: /**
043: * The meta class implementation
044: */
045: public static class MetaClass extends PDFunction.MetaClass {
046: protected MetaClass(Class instanceClass) {
047: super (instanceClass);
048: }
049:
050: protected COSObject doCreateCOSObject() {
051: return COSStream.create(null);
052: }
053: }
054:
055: public static final COSName DK_BitsPerSample = COSName
056: .constant("BitsPerSample"); //$NON-NLS-1$
057:
058: public static final COSName DK_Decode = COSName.constant("Decode"); //$NON-NLS-1$
059:
060: public static final COSName DK_Encode = COSName.constant("Encode"); //$NON-NLS-1$
061:
062: public static final COSName DK_Order = COSName.constant("Order"); //$NON-NLS-1$
063:
064: public static final COSName DK_Size = COSName.constant("Size"); //$NON-NLS-1$
065:
066: /** The meta class instance */
067: public static final MetaClass META = new MetaClass(MetaClass.class
068: .getDeclaringClass());
069:
070: private byte[] samples;
071:
072: /**
073: * PDSampledFunction constructor.
074: *
075: * @param object
076: * the COS object to base this COS based PD object on
077: */
078: protected PDSampledFunction(COSObject object) {
079: super (object);
080: }
081:
082: public COSDictionary cosGetDict() {
083: return cosGetStream().getDict();
084: }
085:
086: /**
087: * Evaluate the function for each of the input values in turn. The output
088: * values are stored in an array and returned.
089: *
090: * @param input
091: * input values to evaluate
092: *
093: * @return an array of output values
094: */
095: public float[] evaluate(float[] input) {
096: float[] output = new float[getOutputSize()];
097: evaluate(input, output);
098: return output;
099: }
100:
101: protected void evaluate(float[] input, float[] output) {
102: float[] intermediate;
103:
104: intermediate = new float[input.length];
105: prepareInput(input, intermediate);
106: lookupInSample(intermediate, output);
107: prepareOutput(output);
108: }
109:
110: /**
111: * Return the BitsPerSample value.
112: *
113: * @return the BitsPerSample value
114: */
115: public int getBitsPerSample() {
116: return cosGetDict().get(DK_BitsPerSample).asNumber().intValue();
117: }
118:
119: /**
120: * Return the Decode value array.
121: *
122: * @return the Decode value array
123: */
124: public COSArray cosGetDecode() {
125: return cosGetDict().get(DK_Decode).asArray();
126: }
127:
128: /**
129: * Return the max value of the Decode array for the given dimension.
130: *
131: * @param dimension
132: * the dimension to get the max value for
133: *
134: * @return the max value of the Decode array for the given dimension
135: */
136: public float getDecodeMax(int dimension) {
137: COSArray decode = cosGetDecode();
138: if (decode == null) {
139: return getRangeMax(dimension);
140: }
141: return decode.get((dimension * 2) + 1).asNumber().floatValue();
142: }
143:
144: /**
145: * Return the min value of the Decode array for the given dimension.
146: *
147: * @param dimension
148: * the dimension to get the min value for
149: *
150: * @return the min value of the Decode array for the given dimension
151: */
152: public float getDecodeMin(int dimension) {
153: COSArray decode = cosGetDecode();
154: if (decode == null) {
155: return getRangeMin(dimension);
156: }
157: return cosGetDecode().get(dimension * 2).asNumber()
158: .floatValue();
159: }
160:
161: /**
162: * Return the Encode value array
163: *
164: * @return the Encode value array
165: */
166: public COSArray cosGetEncode() {
167: return cosGetDict().get(DK_Encode).asArray();
168: }
169:
170: /**
171: * Return the max value of the Encode array for the given dimension.
172: *
173: * @param dimension
174: * the dimension to get the max value for
175: *
176: * @return the max value of the Encode array for the given dimension
177: */
178: public float getEncodeMax(int dimension) {
179: COSArray encode = cosGetEncode();
180: if (encode == null) {
181: return getSize(dimension);
182: }
183: return cosGetEncode().get((dimension * 2) + 1).asNumber()
184: .floatValue();
185: }
186:
187: /**
188: * Return the min value of the Encode array for the given dimension.
189: *
190: * @param dimension
191: * the dimension to get the min value for
192: *
193: * @return the min value of the Encode array for the given dimension
194: */
195: public float getEncodeMin(int dimension) {
196: COSArray encode = cosGetEncode();
197: if (encode == null) {
198: return 0;
199: }
200: return cosGetEncode().get(dimension * 2).asNumber()
201: .floatValue();
202: }
203:
204: /**
205: * Return the number of output values for one input value.
206: *
207: * @return the number of output values for one input value
208: */
209: public int getOutputSize() {
210: return getRange().size() / 2;
211: }
212:
213: /**
214: * Return the sample from the sample stream for the given bit position.
215: *
216: * @param bitPos
217: * the bit position to get the smaple for
218: *
219: * @return the sample from the sample stream for the given bit position
220: */
221: protected int getSample(int bitPos) {
222: int bytePos = bitPos >> 3;
223: int bitShift = (7 - bitPos) & 7;
224: int result = 0;
225: for (int i = 0; i < getBitsPerSample(); i++) {
226: result = result << 1;
227: int sample = (getSamples()[bytePos] >> bitShift) & 1;
228: result = result + sample;
229: if (bitShift == 0) {
230: bytePos++;
231: bitShift = 7;
232: } else {
233: bitShift--;
234: }
235: }
236: return result;
237: }
238:
239: /**
240: * Return the decoded contents of the sample stream.
241: *
242: * @return the decoded contents of the sample stream
243: */
244: protected byte[] getSamples() {
245: if (samples == null) {
246: samples = cosGetStream().getDecodedBytes();
247: }
248: return samples;
249: }
250:
251: /**
252: * Return the Size value array.
253: *
254: * @return the Size value array
255: */
256: public COSArray getSize() {
257: return cosGetDict().get(DK_Size).asArray();
258: }
259:
260: /**
261: * Return the size value for the given dimension.
262: *
263: * @param dimension
264: * the dimension to get the size value for
265: *
266: * @return the size value for the given dimension
267: */
268: public int getSize(int dimension) {
269: return getSize().get(dimension).asNumber().intValue();
270: }
271:
272: protected float interpolate(float x, float xmin, float xmax,
273: float ymin, float ymax) {
274: return ymin + (((x - xmin) * (ymax - ymin)) / (xmax - xmin));
275: }
276:
277: protected void lookupInSample(float[] input, float[] output) {
278: int bits = getBitsPerSample() * getOutputSize();
279: int bitPos = 0;
280: for (int i = input.length - 1; i >= 0; i--) {
281: bitPos = bitPos * getSize(i);
282: int index = (int) input[i];
283: bitPos = bitPos + (bits * index);
284: }
285: for (int j = 0; j < output.length; j++) {
286: output[j] = getSample(bitPos);
287: bitPos = bitPos + getBitsPerSample();
288: }
289: }
290:
291: protected void prepareInput(float[] values, float[] intermediate) {
292: for (int i = 0; i < values.length; i++) {
293: intermediate[i] = clip(values[i], getDomainMin(i),
294: getDomainMax(i));
295: intermediate[i] = interpolate(intermediate[i],
296: getDomainMin(i), getDomainMax(i), getEncodeMin(i),
297: getEncodeMax(i));
298: intermediate[i] = clip(intermediate[i], 0, getSize(i) - 1);
299: }
300: }
301:
302: protected void prepareOutput(float[] values) {
303: for (int i = 0; i < values.length; i++) {
304: values[i] = interpolate(values[i], 0f, (float) Math.pow(2,
305: getBitsPerSample()), getDecodeMin(i),
306: getDecodeMax(i));
307: values[i] = clip(values[i], getRangeMin(i), getRangeMax(i));
308: }
309: }
310: }
|