Source Code Cross Referenced for ErrorDiffusionOpImage.java in  » 6.0-JDK-Modules » Java-Advanced-Imaging » com » sun » media » jai » opimage » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » 6.0 JDK Modules » Java Advanced Imaging » com.sun.media.jai.opimage 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * $RCSfile: ErrorDiffusionOpImage.java,v $
003:         *
004:         * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
005:         *
006:         * Use is subject to license terms.
007:         *
008:         * $Revision: 1.2 $
009:         * $Date: 2005/11/16 17:36:08 $
010:         * $State: Exp $
011:         */
012:        package com.sun.media.jai.opimage;
013:
014:        import java.awt.Point;
015:        import java.awt.Rectangle;
016:        import java.awt.image.ColorModel;
017:        import java.awt.image.DataBuffer;
018:        import java.awt.image.IndexColorModel;
019:        import java.awt.image.MultiPixelPackedSampleModel;
020:        import java.awt.image.Raster;
021:        import java.awt.image.RenderedImage;
022:        import java.awt.image.SampleModel;
023:        import java.awt.image.WritableRaster;
024:        import java.util.Map;
025:        import javax.media.jai.AreaOpImage;
026:        import javax.media.jai.ColorCube;
027:        import javax.media.jai.ImageLayout;
028:        import javax.media.jai.KernelJAI;
029:        import javax.media.jai.LookupTableJAI;
030:        import javax.media.jai.OpImage;
031:        import javax.media.jai.RasterAccessor;
032:        import javax.media.jai.RasterFormatTag;
033:        import javax.media.jai.RasterFactory;
034:        import javax.media.jai.RasterFormatTag;
035:        import javax.media.jai.UntiledOpImage;
036:        import com.sun.media.jai.util.ImageUtil;
037:        import com.sun.media.jai.util.JDKWorkarounds;
038:
039:        /**
040:         * An <code>OpImage</code> implementing the error diffusion operation as
041:         * described in <code>javax.media.jai.operator.ErrorDiffusionDescriptor</code>.
042:         *
043:         * <p>This <code>OpImage</code> performs dithering of its source image into
044:         * a single band image using a specified color map and error filter. For each
045:         * pixel in the source image the nearest entry in the color map is found and
046:         * the index of this entry is assigned to the <code>OpImage</code> at that
047:         * location. The color quantization error is calculated by mapping the index
048:         * back through the color map. The error in each band is then "diffused" to
049:         * other neighboring pixels in the source image according to the specified
050:         * error filter.
051:         *
052:         * @see javax.media.jai.ColorCube
053:         * @see javax.media.jai.KernelJAI
054:         * @see javax.media.jai.LookupTableJAI
055:         *
056:         * @since EA2
057:         *
058:         */
059:        final class ErrorDiffusionOpImage extends UntiledOpImage {
060:            /**
061:             * Smallest float value which when added to unity will yield something
062:             * other than unity.
063:             */
064:            private static final float FLOAT_EPSILON = 1.192092896E-07F;
065:
066:            /**
067:             * Variables used in the optimized case of 3-band byte to 1-band byte
068:             * with a ColorCube color map and a Floyd-Steinberg kernel.
069:             */
070:            private static final int NBANDS = 3;
071:            private static final int NGRAYS = 256;
072:            private static final int OVERSHOOT = 256;
073:            private static final int UNDERSHOOT = 256;
074:            private static final int TOTALGRAYS = (NGRAYS + UNDERSHOOT + OVERSHOOT);
075:            private static final int ERR_SHIFT = 8;
076:
077:            /**
078:             * The color map which maps the <code>ErrorDiffusionOpImage</code> to
079:             * its source.
080:             */
081:            protected LookupTableJAI colorMap;
082:
083:            /**
084:             * The kernel associated with the selected error filter.
085:             */
086:            protected KernelJAI errorKernel;
087:
088:            /**
089:             * The number of bands in the source image.
090:             */
091:            private int numBandsSource;
092:
093:            /**
094:             * Flag indicating whether this is an optimized case.
095:             */
096:            private boolean isOptimizedCase = false;
097:
098:            /**
099:             * Minimum valid pixel value
100:             */
101:            private float minPixelValue;
102:
103:            /**
104:             * Maximum valid pixel value
105:             */
106:            private float maxPixelValue;
107:
108:            /**
109:             * Determines whether a kernel is the Floyd-Steinberg kernel.
110:             *
111:             * @param kernel The <code>KernelJAI</code> to examine.
112:             * @return Whether the kernel argument is the Floyd-Steinberg kernel.
113:             */
114:            private static boolean isFloydSteinbergKernel(KernelJAI kernel) {
115:                int ky = kernel.getYOrigin();
116:
117:                return (kernel.getWidth() == 3
118:                        && kernel.getXOrigin() == 1
119:                        && kernel.getHeight() - ky == 2
120:                        && Math.abs(kernel.getElement(2, ky) - 7.0F / 16.0F) < FLOAT_EPSILON
121:                        && Math
122:                                .abs(kernel.getElement(0, ky + 1) - 3.0F / 16.0F) < FLOAT_EPSILON
123:                        && Math
124:                                .abs(kernel.getElement(1, ky + 1) - 5.0F / 16.0F) < FLOAT_EPSILON && Math
125:                        .abs(kernel.getElement(2, ky + 1) - 1.0F / 16.0F) < FLOAT_EPSILON);
126:            }
127:
128:            /**
129:             * Create the dither table for the 3-band to 1-band byte optimized case.
130:             *
131:             * @param colorCube The color cube to be used in dithering.
132:             * @return The dither table of the optimized algorithm.
133:             */
134:            private static int[] initFloydSteinberg24To8(ColorCube colorCube) {
135:                // Allocate memory for the dither table.
136:                int[] ditherTable = new int[NBANDS * TOTALGRAYS];
137:
138:                float[] thresh = new float[NGRAYS];
139:
140:                //
141:                // Get the colorcube parameters
142:                //
143:                int[] multipliers = colorCube.getMultipliers();
144:                int[] dimsLessOne = colorCube.getDimsLessOne();
145:                int offset = colorCube.getAdjustedOffset();
146:
147:                //
148:                //  Construct tables for each band
149:                //
150:                for (int band = 0; band < NBANDS; band++) {
151:                    int pTab = band * TOTALGRAYS;
152:
153:                    //
154:                    // Calculate the binwidth for this band, i.e. the gray level step
155:                    // from one quantization level to the next. Do this in scaled
156:                    // integer to maintain precision.
157:                    //
158:                    float binWidth = 255.0F / dimsLessOne[band];
159:
160:                    //
161:                    // Pre-calculate the thresholds, so we don't have to do
162:                    // it in the inner loops. The threshold is always the
163:                    // midpoint of each bin, since, in error diffusion, the dithering
164:                    // is done by the error distribution process, not by varying
165:                    // the dither threshold as in ordered dither.
166:                    //
167:                    for (int i = 0; i < dimsLessOne[band]; i++) {
168:                        thresh[i] = (i + 0.5F) * binWidth;
169:                    }
170:                    thresh[dimsLessOne[band]] = 256.0F;
171:
172:                    //
173:                    // Populate the range below gray level zero with the same entry
174:                    // as that for zero. The error distribution can cause undershoots
175:                    // of as much as 255.
176:                    //
177:                    int tableInc = 1 << ERR_SHIFT;
178:                    int tableValue = (-UNDERSHOOT) << ERR_SHIFT;
179:                    for (int gray = -UNDERSHOOT; gray < 0; gray++) {
180:                        ditherTable[pTab++] = tableValue;
181:                        tableValue += tableInc;
182:                    }
183:
184:                    //
185:                    // Populate the main range of 0...255.
186:                    //
187:                    int indexContrib = 0;
188:                    float frepValue = 0.0F;
189:                    int repValue;
190:                    int binNum = 0;
191:                    float threshold = thresh[0];
192:                    int gray = 0;
193:                    while (gray < 256) {
194:                        //
195:                        // Populate all the table values up to the next threshold.
196:                        // Since the only thing which changes is the error,
197:                        // and it changes by one scaled gray level, we can
198:                        // just add the increment at each iteration.
199:                        //
200:                        int tableBase = indexContrib;
201:                        repValue = (int) (frepValue + 0.5F);
202:                        while ((float) gray < threshold) {
203:                            ditherTable[pTab++] = ((gray - repValue) << ERR_SHIFT)
204:                                    + tableBase;
205:                            gray++;
206:                        }
207:
208:                        //
209:                        // Once the gray level crosses a threshold,
210:                        // move to the next bin threshold. Also update
211:                        // the color contribution index step and the
212:                        // representative value, needed to compute the error.
213:                        //
214:                        threshold = thresh[++binNum];
215:                        indexContrib += multipliers[band];
216:                        frepValue += binWidth;
217:                    }
218:
219:                    //
220:                    // Populate the range above gray level 255 with the same entry
221:                    // as that for 255. As in the under-range case, the error
222:                    // distribution can cause overshoots as high as 255 over max.
223:                    //
224:                    indexContrib -= multipliers[band];
225:                    repValue = 255;
226:                    tableValue = ((256 - repValue) << ERR_SHIFT) | indexContrib;
227:
228:                    for (gray = 256; gray < (256 + OVERSHOOT); gray++) {
229:                        ditherTable[pTab++] = tableValue;
230:                        tableValue += tableInc;
231:                    }
232:
233:                } // End band loop
234:
235:                //
236:                // Add in the colormap offset value to the index contribution
237:                // for the first band. This eliminates the need to add it in
238:                // when we do the error diffusion.
239:                //
240:                int pTab = 0;
241:                for (int count = TOTALGRAYS; count != 0; count--) {
242:                    ditherTable[pTab] += offset;
243:                    pTab++;
244:                }
245:
246:                return ditherTable;
247:            }
248:
249:            /**
250:             * Force the destination image to be single-banded.
251:             */
252:            private static ImageLayout layoutHelper(ImageLayout layout,
253:                    RenderedImage source, LookupTableJAI colorMap) {
254:                // Create or clone the layout.
255:                ImageLayout il = layout == null ? new ImageLayout()
256:                        : (ImageLayout) layout.clone();
257:
258:                // Force the destination and source origins and dimensions to coincide.
259:                il.setMinX(source.getMinX());
260:                il.setMinY(source.getMinY());
261:                il.setWidth(source.getWidth());
262:                il.setHeight(source.getHeight());
263:
264:                // Get the SampleModel.
265:                SampleModel sm = il.getSampleModel(source);
266:
267:                // Ensure an appropriate SampleModel.
268:                if (colorMap.getNumBands() == 1
269:                        && colorMap.getNumEntries() == 2
270:                        && !ImageUtil.isBinary(il.getSampleModel(source))) {
271:                    sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE,
272:                            il.getTileWidth(source), il.getTileHeight(source),
273:                            1);
274:                    il.setSampleModel(sm);
275:                }
276:
277:                // Make sure that this OpImage is single-banded.
278:                if (sm.getNumBands() != 1) {
279:                    sm = RasterFactory.createComponentSampleModel(sm, sm
280:                            .getTransferType(), sm.getWidth(), sm.getHeight(),
281:                            1);
282:                    il.setSampleModel(sm);
283:
284:                    // Clear the ColorModel mask if needed.
285:                    ColorModel cm = il.getColorModel(null);
286:                    if (cm != null
287:                            && !JDKWorkarounds.areCompatibleDataModels(sm, cm)) {
288:                        // Clear the mask bit if incompatible.
289:                        il.unsetValid(ImageLayout.COLOR_MODEL_MASK);
290:                    }
291:                }
292:
293:                // Determine whether a larger bit depth is needed.
294:                int numColorMapBands = colorMap.getNumBands();
295:                int maxIndex = 0;
296:                for (int i = 0; i < numColorMapBands; i++) {
297:                    maxIndex = Math.max(colorMap.getOffset(i)
298:                            + colorMap.getNumEntries() - 1, maxIndex);
299:                }
300:
301:                // Create a deeper SampleModel if needed.
302:                if ((maxIndex > 255 && sm.getDataType() == DataBuffer.TYPE_BYTE)
303:                        || (maxIndex > 65535 && sm.getDataType() != DataBuffer.TYPE_INT)) {
304:                    int dataType = maxIndex > 65535 ? DataBuffer.TYPE_INT
305:                            : DataBuffer.TYPE_USHORT;
306:                    sm = RasterFactory.createComponentSampleModel(sm, dataType,
307:                            sm.getWidth(), sm.getHeight(), 1);
308:                    il.setSampleModel(sm);
309:
310:                    // Clear the ColorModel mask if needed.
311:                    ColorModel cm = il.getColorModel(null);
312:                    if (cm != null
313:                            && !JDKWorkarounds.areCompatibleDataModels(sm, cm)) {
314:                        // Clear the mask bit if incompatible.
315:                        il.unsetValid(ImageLayout.COLOR_MODEL_MASK);
316:                    }
317:                }
318:
319:                // Set an IndexColorModel on the image if:
320:                // a. none is provided in the layout;
321:                // b. source and colormap have byte data type;
322:                // c. the colormap has 3 bands;
323:                // d. destination has byte or ushort data type.
324:                if ((layout == null || !il
325:                        .isValid(ImageLayout.COLOR_MODEL_MASK))
326:                        && source.getSampleModel().getDataType() == DataBuffer.TYPE_BYTE
327:                        && (sm.getDataType() == DataBuffer.TYPE_BYTE || sm
328:                                .getDataType() == DataBuffer.TYPE_USHORT)
329:                        && colorMap.getDataType() == DataBuffer.TYPE_BYTE
330:                        && colorMap.getNumBands() == 3) {
331:                    ColorModel cm = source.getColorModel();
332:                    if (cm == null
333:                            || (cm != null && cm.getColorSpace().isCS_sRGB())) {
334:                        int size = colorMap.getNumEntries();
335:                        byte[][] cmap = new byte[3][maxIndex + 1];
336:                        for (int i = 0; i < 3; i++) {
337:                            byte[] band = cmap[i];
338:                            byte[] data = colorMap.getByteData(i);
339:                            int offset = colorMap.getOffset(i);
340:                            int end = offset + size;
341:                            for (int j = offset; j < end; j++) {
342:                                band[j] = data[j - offset];
343:                            }
344:                        }
345:
346:                        int numBits = sm.getDataType() == DataBuffer.TYPE_BYTE ? 8
347:                                : 16;
348:                        il.setColorModel(new IndexColorModel(numBits,
349:                                maxIndex + 1, cmap[0], cmap[1], cmap[2]));
350:                    }
351:                }
352:
353:                return il;
354:            }
355:
356:            /**
357:             * Constructs an ErrorDiffusionOpImage object.
358:             *
359:             * <p>The image dimensions are derived from the source image. The tile
360:             * grid layout, SampleModel, and ColorModel may optionally be specified
361:             * by an ImageLayout object. The calculation assumes that the entire
362:             * color quantization error is distributed to the right and below the
363:             * current pixel and the filter kernel values are handled appropriately.
364:             *
365:             * @param source A RenderedImage.
366:             * @param layout An ImageLayout optionally containing the tile grid layout,
367:             * SampleModel, and ColorModel, or null.
368:             * @param colorMap The color map to use which must have a number of bands
369:             * equal to the number of bands in the source image. The offset of this
370:             * <code>LookupTableJAI</code> must be the same for all bands.
371:             * @param errorKernel The error filter kernel. This must have values
372:             * between 0.0 and 1.0. Only the entries to the right of and on the same
373:             * row as the key entry, and those entries below of the row of the key
374:             * entry are used; all other values are ignored. The values used must sum
375:             * to 1.0. Note that if a 1-by-1 error filter kernel is supplied, the value
376:             * of the unique kernel element is irrelevant and the output of the
377:             * algorithm will simply be the index in the supplied color map of the
378:             * nearest matching color to the source pixel at the same position.
379:             */
380:            public ErrorDiffusionOpImage(RenderedImage source, Map config,
381:                    ImageLayout layout, LookupTableJAI colorMap,
382:                    KernelJAI errorKernel) {
383:                super (source, config, layoutHelper(layout, source, colorMap));
384:
385:                // Get the source sample model.
386:                SampleModel srcSampleModel = source.getSampleModel();
387:
388:                // Cache the number of bands in the source.
389:                numBandsSource = srcSampleModel.getNumBands();
390:
391:                // Set a reference to the LookupTableJAI.
392:                this .colorMap = colorMap;
393:
394:                // Set a reference to the KernelJAI.
395:                this .errorKernel = errorKernel;
396:
397:                // Determine whether this is an (read "the") optimized case.
398:                isOptimizedCase = (sampleModel.getTransferType() == DataBuffer.TYPE_BYTE
399:                        && srcSampleModel.getTransferType() == DataBuffer.TYPE_BYTE
400:                        && numBandsSource == 3 && colorMap instanceof  ColorCube && isFloydSteinbergKernel(errorKernel));
401:
402:                // Determine minumum and maximum valid pixel values
403:                switch (colorMap.getDataType()) {
404:                case DataBuffer.TYPE_BYTE:
405:                    // Treat byte types as unsigned bytes
406:                    minPixelValue = 0;
407:                    maxPixelValue = -Byte.MIN_VALUE + Byte.MAX_VALUE;
408:                    break;
409:                case DataBuffer.TYPE_SHORT:
410:                    minPixelValue = Short.MIN_VALUE;
411:                    maxPixelValue = Short.MAX_VALUE;
412:                    break;
413:                case DataBuffer.TYPE_USHORT:
414:                    minPixelValue = 0;
415:                    maxPixelValue = -Short.MIN_VALUE + Short.MAX_VALUE;
416:                    break;
417:                case DataBuffer.TYPE_INT:
418:                    minPixelValue = Integer.MIN_VALUE;
419:                    maxPixelValue = Integer.MAX_VALUE;
420:                    break;
421:                case DataBuffer.TYPE_FLOAT:
422:                    minPixelValue = 0;
423:                    maxPixelValue = Float.MAX_VALUE;
424:                    break;
425:                case DataBuffer.TYPE_DOUBLE:
426:                    minPixelValue = 0;
427:                    maxPixelValue = Float.MAX_VALUE;
428:                    break;
429:                default:
430:                    throw new RuntimeException(JaiI18N
431:                            .getString("ErrorDiffusionOpImage0"));
432:                }
433:
434:            }
435:
436:            /**
437:             * Performs error diffusion on a specified rectangle. The sources are
438:             * cobbled. As error diffusion must be calculated on a line-by-line basis
439:             * starting at the upper left corner of the image, all image lines through
440:             * and including the last line of the tile containing the requested
441:             * <code>Rectangle</code> are calculated.
442:             *
443:             * @param sources The source image Raster.
444:             * @param dest A WritableRaster tile containing the area to be computed.
445:             * @param destRect The rectangle within dest to be processed.
446:             */
447:            protected void computeImage(Raster[] sources, WritableRaster dest,
448:                    Rectangle destRect) {
449:                Raster source = sources[0];
450:
451:                if (isOptimizedCase) {
452:                    computeImageOptimized(source, dest, destRect);
453:                } else {
454:                    computeImageDefault(source, dest, destRect);
455:                }
456:            }
457:
458:            protected void computeImageDefault(Raster source,
459:                    WritableRaster dest, Rectangle destRect) {
460:                // Set X-coordinate range.
461:                int startX = minX;
462:                int endX = startX + width - 1;
463:
464:                // Set Y-coordinate range.
465:                int startY = minY;
466:                int endY = startY + height - 1;
467:
468:                // Set the number of lines in the calculation buffer.
469:                int numLinesBuffer = errorKernel.getHeight()
470:                        - errorKernel.getYOrigin();
471:
472:                // Allocate memory for the calculation buffer.
473:                float[][] bufMem = new float[numLinesBuffer][width
474:                        * numBandsSource];
475:
476:                // Allocate memory for the buffer index array.
477:                int[] bufIdx = new int[numLinesBuffer];
478:
479:                // Initialize the buffer index array and the rolling buffer.
480:                for (int idx = 0; idx < numLinesBuffer; idx++) {
481:                    bufIdx[idx] = idx;
482:                    source.getPixels(startX, startY + idx, width, 1,
483:                            bufMem[idx]);
484:                }
485:
486:                // Set variable to indicate index of last rolling buffer line.
487:                int lastLineBuffer = numLinesBuffer - 1;
488:
489:                // Initialize some kernel-dependent constants.
490:                int kernelWidth = errorKernel.getWidth();
491:                float[] kernelData = errorKernel.getKernelData();
492:                int diffuseRight = kernelWidth - errorKernel.getXOrigin() - 1;
493:                int diffuseBelow = errorKernel.getHeight()
494:                        - errorKernel.getYOrigin() - 1;
495:                int kernelOffsetRight = errorKernel.getYOrigin() * kernelWidth
496:                        + errorKernel.getXOrigin() + 1;
497:                int kernelOffsetBelow = (errorKernel.getYOrigin() + 1)
498:                        * kernelWidth;
499:
500:                // Set up some arrays for looping.
501:                float[] currentPixel = new float[numBandsSource];
502:                int offset = colorMap.getOffset();
503:                float[] qError = new float[numBandsSource];
504:
505:                // Loop over lines.
506:                int[] dstData = new int[width];
507:                for (int y = startY; y <= endY; y++) {
508:                    int currentIndex = bufIdx[0];
509:                    float[] currentLine = bufMem[currentIndex];
510:
511:                    // Loop over pixels.
512:                    int dstOffset = 0;
513:                    for (int x = startX, z = 0; x <= endX; x++) {
514:                        // Copy all samples of the current pixel.
515:                        for (int b = 0; b < numBandsSource; b++) {
516:                            currentPixel[b] = currentLine[z++];
517:
518:                            // Clamp the current sample to the valid range
519:                            if (currentPixel[b] < minPixelValue
520:                                    || currentPixel[b] > maxPixelValue) {
521:                                currentPixel[b] = java.lang.Math.max(
522:                                        currentPixel[b], minPixelValue);
523:                                currentPixel[b] = java.lang.Math.min(
524:                                        currentPixel[b], maxPixelValue);
525:                            }
526:                        }
527:
528:                        // Find the index of the nearest color in the map.
529:                        int nearestIndex = colorMap
530:                                .findNearestEntry(currentPixel);
531:
532:                        // Save the index in the output data buffer.
533:                        dstData[dstOffset++] = nearestIndex;
534:
535:                        // Calculate the error between the nearest and actual colors.
536:                        boolean isQuantizationError = false;
537:                        for (int b = 0; b < numBandsSource; b++) {
538:                            qError[b] = currentPixel[b]
539:                                    - colorMap.lookupFloat(b, nearestIndex);
540:                            if (qError[b] != 0.0F) {
541:                                isQuantizationError = true;
542:                            }
543:                        }
544:
545:                        // If there was error in at least one band, distribute it.
546:                        if (isQuantizationError) {
547:                            // Distribute error to the right of key entry.
548:                            int rightCount = Math.min(diffuseRight, endX - x);
549:                            int kernelOffset = kernelOffsetRight;
550:                            int sampleOffset = z;
551:                            for (int u = 1; u <= rightCount; u++) {
552:                                for (int b = 0; b < numBandsSource; b++) {
553:                                    currentLine[sampleOffset++] += qError[b]
554:                                            * kernelData[kernelOffset];
555:                                }
556:                                kernelOffset++;
557:                            }
558:
559:                            // Distribute error below key entry.
560:                            int offsetLeft = Math.min(x - startX, diffuseRight);
561:                            int count = Math.min(x + diffuseRight, endX)
562:                                    - Math.max(x - diffuseRight, startX) + 1;
563:                            for (int v = 1; v <= diffuseBelow; v++) {
564:                                float[] line = bufMem[bufIdx[v]];
565:                                kernelOffset = kernelOffsetBelow;
566:                                sampleOffset = z - (offsetLeft + 1)
567:                                        * numBandsSource;
568:                                for (int u = 1; u <= count; u++) {
569:                                    for (int b = 0; b < numBandsSource; b++) {
570:                                        line[sampleOffset++] += qError[b]
571:                                                * kernelData[kernelOffset];
572:                                    }
573:                                    kernelOffset++;
574:                                }
575:                            }
576:                        }
577:                    }
578:
579:                    //
580:                    // Save data for the current destination line.
581:                    //
582:                    dest.setSamples(startX, y, destRect.width, 1, 0, dstData);
583:
584:                    // Rotate the buffer indexes.
585:                    for (int k = 0; k < lastLineBuffer; k++) {
586:                        bufIdx[k] = bufIdx[k + 1];
587:                    }
588:                    bufIdx[lastLineBuffer] = currentIndex;
589:
590:                    // If available, load next image line into the last buffer line.
591:                    if (y + numLinesBuffer < getMaxY()) {
592:                        source.getPixels(startX, y + numLinesBuffer, width, 1,
593:                                bufMem[bufIdx[lastLineBuffer]]);
594:                    }
595:                }
596:            }
597:
598:            protected void computeImageOptimized(Raster source,
599:                    WritableRaster dest, Rectangle destRect) {
600:                // Set X-coordinate range.
601:                int startX = minX;
602:                int endX = startX + width - 1;
603:
604:                // Set Y-coordinate range.
605:                int startY = minY;
606:                int endY = startY + height - 1;
607:
608:                // Initialize the dither table.
609:                int[] ditherTable = initFloydSteinberg24To8((ColorCube) colorMap);
610:
611:                // Initialize the padded source width.
612:                int sourceWidthPadded = source.getWidth() + 2;
613:
614:                // Allocate memory for the error buffer.
615:                int[] errBuf = new int[sourceWidthPadded * NBANDS];
616:
617:                // Retrieve format tags.
618:                RasterFormatTag[] formatTags = getFormatTags();
619:
620:                RasterAccessor srcAccessor = new RasterAccessor(source,
621:                        new Rectangle(startX, startY, source.getWidth(), source
622:                                .getHeight()), formatTags[0], getSourceImage(0)
623:                                .getColorModel());
624:                RasterAccessor dstAccessor = new RasterAccessor(dest, destRect,
625:                        formatTags[1], getColorModel());
626:
627:                // Set pixel and line strides.
628:                int srcPixelStride = srcAccessor.getPixelStride();
629:                int srcScanlineStride = srcAccessor.getScanlineStride();
630:                int dstPixelStride = dstAccessor.getPixelStride();
631:                int dstScanlineStride = dstAccessor.getScanlineStride();
632:
633:                // Set data arrays.
634:                byte[] srcData0 = srcAccessor.getByteDataArray(0);
635:                byte[] srcData1 = srcAccessor.getByteDataArray(1);
636:                byte[] srcData2 = srcAccessor.getByteDataArray(2);
637:                byte[] dstData = dstAccessor.getByteDataArray(0);
638:
639:                // Initialize line offset in each band.
640:                int srcLine0 = srcAccessor.getBandOffset(0);
641:                int srcLine1 = srcAccessor.getBandOffset(1);
642:                int srcLine2 = srcAccessor.getBandOffset(2);
643:                int dstLine = dstAccessor.getBandOffset(0);
644:
645:                //
646:                //  For each line, calculate and distribute the error into
647:                //  a 3 line error buffer (one line for each band).
648:                //  Also accumulate the contributions of the 3 bands
649:                //  into the same line of the temporary output buffer.
650:                //
651:                //  The error buffer starts out with all zeroes as the
652:                //  amount of error to propagate forward.
653:                //
654:                for (int y = startY; y <= endY; y++) {
655:                    // Initialize pixel offset in each line in each band.
656:                    int srcPixel0 = srcLine0;
657:                    int srcPixel1 = srcLine1;
658:                    int srcPixel2 = srcLine2;
659:                    int dstPixel = dstLine;
660:
661:                    //
662:                    // Determine the error and index contribution for
663:                    // the each band. Keep the transitory errors
664:                    // (errA, errC and errD) in local variables
665:                    // (hopefully registers). The calculated value
666:                    // of errB gets put into the error buffer, to be used
667:                    // on the next line.
668:                    //
669:                    // This is the logic here. Floyd-Steinberg dithering
670:                    // distributes errors to four neighboring pixels,
671:                    // as shown below. X is the pixel being operated on.
672:                    //
673:                    //    7/16 of the error goes to pixel A
674:                    //    3/16 of the error goes to pixel B
675:                    //    5/16 of the error goes to pixel C
676:                    //    1/16 of the error goes to pixel D
677:                    //
678:                    //         X A
679:                    //       B C D
680:                    //
681:                    // The error distributed to pixel A is reused immediately
682:                    // in the calculation of the next pixel on the same line.
683:                    // The errors distributed to B, C and D will be used on the
684:                    // following line. As we move from left to right, the
685:                    // new error distributed to B gets added to the error
686:                    // at the previous C. Likewise, the new C error gets added
687:                    // to the previous D error. So only the errors propagating
688:                    // to position B survive in the saved error buffer. The
689:                    // only exception is at the line end, where error C must be
690:                    // saved. The scheme is shown below.
691:                    //
692:                    //      XA
693:                    //     BCD
694:                    //      BCD
695:                    //       BCD
696:                    //        BCD
697:                    //
698:                    // Treat the error buffer as pixel sequential.
699:                    // This lets us use a single pointer with offsets
700:                    // for the entries for all three bands.
701:                    //
702:
703:                    //
704:                    // Zero the error holders for all bands
705:                    // The bands are called Red, Grn and Blu, but are
706:                    // really just the first, second and third bands.
707:                    //
708:                    int errRedA = 0;
709:                    int errRedC = 0;
710:                    int errRedD = 0;
711:                    int errGrnA = 0;
712:                    int errGrnC = 0;
713:                    int errGrnD = 0;
714:                    int errBluA = 0;
715:                    int errBluC = 0;
716:                    int errBluD = 0;
717:
718:                    int pErr = 0;
719:                    int dstOffset = 0;
720:                    for (int x = startX; x <= endX; x++) {
721:                        //
722:                        // First band (Red)
723:                        // The color index is initialized here.
724:                        // Set the table pointer to the "Red" band
725:                        //
726:                        int pTab = UNDERSHOOT;
727:
728:                        int adjVal = ((errRedA + errBuf[pErr + 3] + 8) >> 4)
729:                                + (int) (srcData0[srcPixel0] & 0xff);
730:                        srcPixel0 += srcPixelStride;
731:                        int tabval = ditherTable[pTab + adjVal];
732:                        int err = tabval >> 8;
733:                        int err1 = err;
734:                        int index = (tabval & 0xff);
735:                        int err2 = err + err;
736:                        errBuf[pErr] = errRedC + (err += err2); // 3/16 (B)
737:                        errRedC = errRedD + (err += err2); // 5/16 (C)
738:                        errRedD = err1; // 1/16 (D)
739:                        errRedA = (err += err2); // 7/16 (A)
740:
741:                        //
742:                        // Second band (Green)
743:                        // Set the table pointer to the "Green" band
744:                        // The color index is incremented here.
745:                        //
746:                        pTab += TOTALGRAYS;
747:
748:                        adjVal = ((errGrnA + errBuf[pErr + 4] + 8) >> 4)
749:                                + (int) (srcData1[srcPixel1] & 0xff);
750:                        srcPixel1 += srcPixelStride;
751:                        tabval = ditherTable[pTab + adjVal];
752:                        err = tabval >> 8;
753:                        err1 = err;
754:                        index += (tabval & 0xff);
755:                        err2 = err + err;
756:                        errBuf[pErr + 1] = errGrnC + (err += err2);
757:                        errGrnC = errGrnD + (err += err2);
758:                        errGrnD = err1;
759:                        errGrnA = (err += err2);
760:
761:                        pTab += TOTALGRAYS;
762:
763:                        //
764:                        // Third band (Blue)
765:                        // Set the table pointer to the "Blue" band
766:                        // The color index is incremented here.
767:                        //
768:                        adjVal = ((errBluA + errBuf[pErr + 5] + 8) >> 4)
769:                                + (int) (srcData2[srcPixel2] & 0xff);
770:                        srcPixel2 += srcPixelStride;
771:                        tabval = ditherTable[pTab + adjVal];
772:                        err = tabval >> 8;
773:                        err1 = err;
774:                        index += (tabval & 0xff);
775:                        err2 = err + err;
776:                        errBuf[pErr + 2] = errBluC + (err += err2);
777:                        errBluC = errBluD + (err += err2);
778:                        errBluD = err1;
779:                        errBluA = (err += err2);
780:
781:                        // Save the result in the output data buffer.
782:                        dstData[dstPixel] = (byte) (index & 0xff);
783:                        dstPixel += dstPixelStride;
784:
785:                        pErr += 3;
786:
787:                    } // End pixel loop
788:
789:                    //
790:                    // Save last error in line
791:                    //
792:                    int last = 3 * (sourceWidthPadded - 2);
793:                    errBuf[last] = errRedC;
794:                    errBuf[last + 1] = errGrnC;
795:                    errBuf[last + 2] = errBluC;
796:
797:                    // Increment offset in each band to next line.
798:                    srcLine0 += srcScanlineStride;
799:                    srcLine1 += srcScanlineStride;
800:                    srcLine2 += srcScanlineStride;
801:                    dstLine += dstScanlineStride;
802:                } // End scanline loop
803:
804:                // Make sure that the output data is copied to the destination.
805:                dstAccessor.copyDataToRaster();
806:            }
807:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.