KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > ext > awt > image > GraphicsUtil


1 /*
2
3    Copyright 2001-2004 The Apache Software Foundation
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16
17  */

18 package org.apache.batik.ext.awt.image;
19
20 import java.awt.Composite JavaDoc;
21 import java.awt.Graphics2D JavaDoc;
22 import java.awt.GraphicsConfiguration JavaDoc;
23 import java.awt.GraphicsDevice JavaDoc;
24 import java.awt.Point JavaDoc;
25 import java.awt.Rectangle JavaDoc;
26 import java.awt.RenderingHints JavaDoc;
27 import java.awt.Shape JavaDoc;
28 import java.awt.color.ColorSpace JavaDoc;
29 import java.awt.geom.AffineTransform JavaDoc;
30 import java.awt.geom.Rectangle2D JavaDoc;
31 import java.awt.image.BufferedImage JavaDoc;
32 import java.awt.image.ColorModel JavaDoc;
33 import java.awt.image.ComponentSampleModel JavaDoc;
34 import java.awt.image.DataBuffer JavaDoc;
35 import java.awt.image.DataBufferByte JavaDoc;
36 import java.awt.image.DataBufferInt JavaDoc;
37 import java.awt.image.DataBufferShort JavaDoc;
38 import java.awt.image.DataBufferUShort JavaDoc;
39 import java.awt.image.DirectColorModel JavaDoc;
40 import java.awt.image.Raster JavaDoc;
41 import java.awt.image.RenderedImage JavaDoc;
42 import java.awt.image.SampleModel JavaDoc;
43 import java.awt.image.SinglePixelPackedSampleModel JavaDoc;
44 import java.awt.image.WritableRaster JavaDoc;
45 import java.awt.image.renderable.RenderContext JavaDoc;
46 import java.awt.image.renderable.RenderableImage JavaDoc;
47 import java.lang.ref.Reference JavaDoc;
48 import java.lang.ref.WeakReference JavaDoc;
49
50 import org.apache.batik.ext.awt.RenderingHintsKeyExt;
51 import org.apache.batik.ext.awt.image.renderable.PaintRable;
52 import org.apache.batik.ext.awt.image.rendered.AffineRed;
53 import org.apache.batik.ext.awt.image.rendered.Any2LsRGBRed;
54 import org.apache.batik.ext.awt.image.rendered.Any2sRGBRed;
55 import org.apache.batik.ext.awt.image.rendered.BufferedImageCachableRed;
56 import org.apache.batik.ext.awt.image.rendered.CachableRed;
57 import org.apache.batik.ext.awt.image.rendered.FormatRed;
58 import org.apache.batik.ext.awt.image.rendered.RenderedImageCachableRed;
59 import org.apache.batik.ext.awt.image.rendered.TranslateRed;
60
61
62 /**
63  * Set of utility methods for Graphics.
64  * These generally bypass broken methods in Java2D or provide tweaked
65  * implementations.
66  *
67  * @author <a HREF="mailto:Thomas.DeWeeese@Kodak.com">Thomas DeWeese</a>
68  * @version $Id: GraphicsUtil.java,v 1.36 2005/03/27 08:58:32 cam Exp $
69  */

70 public class GraphicsUtil {
71
72     public static AffineTransform JavaDoc IDENTITY = new AffineTransform JavaDoc();
73
74     /**
75      * Draws <tt>ri</tt> into <tt>g2d</tt>. It does this be
76      * requesting tiles from <tt>ri</tt> and drawing them individually
77      * in <tt>g2d</tt> it also takes care of some colorspace and alpha
78      * issues.
79      * @param g2d The Graphics2D to draw into.
80      * @param ri The image to be drawn.
81      */

82     public static void drawImage(Graphics2D JavaDoc g2d,
83                                  RenderedImage JavaDoc ri) {
84         drawImage(g2d, wrap(ri));
85     }
86
87     /**
88      * Draws <tt>cr</tt> into <tt>g2d</tt>. It does this be
89      * requesting tiles from <tt>ri</tt> and drawing them individually
90      * in <tt>g2d</tt> it also takes care of some colorspace and alpha
91      * issues.
92      * @param g2d The Graphics2D to draw into.
93      * @param cr The image to be drawn.
94      */

95     public static void drawImage(Graphics2D JavaDoc g2d,
96                                  CachableRed cr) {
97
98         // System.out.println("DrawImage G: " + g2d);
99

100         AffineTransform JavaDoc at = null;
101         while (true) {
102             if (cr instanceof AffineRed) {
103                 AffineRed ar = (AffineRed)cr;
104                 if (at == null)
105                     at = ar.getTransform();
106                 else
107                     at.concatenate(ar.getTransform());
108                 cr = ar.getSource();
109                 continue;
110             } else if (cr instanceof TranslateRed) {
111                 TranslateRed tr = (TranslateRed)cr;
112                 // System.out.println("testing Translate");
113
int dx = tr.getDeltaX();
114                 int dy = tr.getDeltaY();
115                 if (at == null)
116                     at = AffineTransform.getTranslateInstance(dx, dy);
117                 else
118                     at.translate(dx, dy);
119                 cr = tr.getSource();
120                 continue;
121             }
122             break;
123         }
124         AffineTransform JavaDoc g2dAt = g2d.getTransform();
125         if ((at == null) || (at.isIdentity()))
126             at = g2dAt;
127         else
128             at.preConcatenate(g2dAt);
129
130         ColorModel JavaDoc srcCM = cr.getColorModel();
131         ColorSpace JavaDoc g2dCS = getDestinationColorSpace(g2d);
132         ColorModel JavaDoc g2dCM = getDestinationColorModel(g2d);
133         if (g2dCS == null)
134             // Assume device is sRGB
135
g2dCS = ColorSpace.getInstance(ColorSpace.CS_sRGB);
136         ColorModel JavaDoc drawCM = g2dCM;
137         if ((g2dCM == null) || !g2dCM.hasAlpha()) {
138             // If we can't find out about our device or the device
139
// does not support alpha just use SRGB unpremultiplied
140
// (Just because this seems to work for us).
141
drawCM = sRGB_Unpre;
142         }
143
144         if (cr instanceof BufferedImageCachableRed) {
145             // There is a huge win if we can use the BI directly here.
146
// This results in something like a 10x performance gain
147
// for images, the best thing is this is the common case.
148
if (g2dCS.equals(srcCM.getColorSpace()) &&
149                 drawCM.equals(srcCM)) {
150                 // System.err.println("Fast Case");
151
g2d.setTransform(at);
152                 BufferedImageCachableRed bicr;
153                 bicr = (BufferedImageCachableRed)cr;
154                 g2d.drawImage(bicr.getBufferedImage(),
155                               bicr.getMinX(), bicr.getMinY(), null);
156                 g2d.setTransform(g2dAt);
157                 return;
158             }
159         }
160
161         // Scaling down so do it before color conversion.
162
double determinant = at.getDeterminant();
163         if (!at.isIdentity() && (determinant <= 1.0)) {
164             if (at.getType() != AffineTransform.TYPE_TRANSLATION)
165                 cr = new AffineRed(cr, at, g2d.getRenderingHints());
166             else {
167                 int xloc = cr.getMinX() + (int)at.getTranslateX();
168                 int yloc = cr.getMinY() + (int)at.getTranslateY();
169                 cr = new TranslateRed(cr, xloc, yloc);
170             }
171         }
172
173         if (g2dCS != srcCM.getColorSpace()) {
174             // System.out.println("srcCS: " + srcCM.getColorSpace());
175
// System.out.println("g2dCS: " + g2dCS);
176
// System.out.println("sRGB: " +
177
// ColorSpace.getInstance(ColorSpace.CS_sRGB));
178
// System.out.println("LsRGB: " +
179
// ColorSpace.getInstance
180
// (ColorSpace.CS_LINEAR_RGB));
181
if (g2dCS == ColorSpace.getInstance(ColorSpace.CS_sRGB))
182                 cr = convertTosRGB(cr);
183             else if (g2dCS == ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB))
184                 cr = convertToLsRGB(cr);
185         }
186         srcCM = cr.getColorModel();
187         if (!drawCM.equals(srcCM))
188             cr = FormatRed.construct(cr, drawCM);
189
190         // Scaling up so do it after color conversion.
191
if (!at.isIdentity() && (determinant > 1.0))
192             cr = new AffineRed(cr, at, g2d.getRenderingHints());
193
194         // Now CR is in device space, so clear the g2d transform.
195
g2d.setTransform(IDENTITY);
196
197         // Ugly Hack alert. This Makes it use our SrcOver implementation
198
// Which doesn't seem to have as many bugs as the JDK one when
199
// going between different src's and destinations (of course it's
200
// also a lot slower).
201
Composite JavaDoc g2dComposite = g2d.getComposite();
202         if (g2d.getRenderingHint(RenderingHintsKeyExt.KEY_TRANSCODING) ==
203             RenderingHintsKeyExt.VALUE_TRANSCODING_PRINTING) {
204             if (SVGComposite.OVER.equals(g2dComposite)) {
205                 g2d.setComposite(SVGComposite.OVER);
206             }
207         }
208         Rectangle JavaDoc crR = cr.getBounds();
209         Shape JavaDoc clip = g2d.getClip();
210
211         try {
212             Rectangle JavaDoc clipR;
213             if (clip == null) {
214                 clip = crR;
215                 clipR = crR;
216             } else {
217                 clipR = clip.getBounds();
218
219                 if (clipR.intersects(crR) == false)
220                     return; // Nothing to draw...
221
clipR = clipR.intersection(crR);
222             }
223
224             Rectangle JavaDoc gcR = getDestinationBounds(g2d);
225             // System.out.println("ClipRects: " + clipR + " -> " + gcR);
226
if (gcR != null) {
227                 if (clipR.intersects(gcR) == false)
228                     return; // Nothing to draw...
229
clipR = clipR.intersection(gcR);
230             }
231
232             // System.out.println("Starting Draw: " + cr);
233
// long startTime = System.currentTimeMillis();
234

235             boolean useDrawRenderedImage = false;
236
237             srcCM = cr.getColorModel();
238             SampleModel JavaDoc srcSM = cr.getSampleModel();
239             if ((srcSM.getWidth()*srcSM.getHeight()) >=
240                 (clipR.width*clipR.height))
241                 // if srcSM tiles are around the clip size
242
// then just draw the renderedImage
243
useDrawRenderedImage = true;
244
245             Object JavaDoc atpHint = g2d.getRenderingHint
246                 (RenderingHintsKeyExt.KEY_AVOID_TILE_PAINTING);
247
248             if (atpHint == RenderingHintsKeyExt.VALUE_AVOID_TILE_PAINTING_ON)
249                 useDrawRenderedImage = true; //for PDF and PS transcoders
250

251             if (atpHint == RenderingHintsKeyExt.VALUE_AVOID_TILE_PAINTING_OFF)
252                 useDrawRenderedImage = false;
253
254
255             WritableRaster JavaDoc wr;
256             if (useDrawRenderedImage) {
257                 // This can be significantly faster but can also
258
// require much more memory, so we only use it when
259
// the clip size is smaller than the tile size.
260
Raster JavaDoc r = cr.getData(clipR);
261                 wr = ((WritableRaster JavaDoc)r).createWritableChild
262                     (clipR.x, clipR.y, clipR.width, clipR.height,
263                      0, 0, null);
264                 
265                 BufferedImage JavaDoc bi = new BufferedImage JavaDoc
266                     (srcCM, wr, srcCM.isAlphaPremultiplied(), null);
267                 
268                 // Any of the drawImage calls that take an
269
// Affine are prone to the 'CGGStackRestore: gstack
270
// underflow' bug on Mac OS X. This should work
271
// around that problem.
272
g2d.drawImage(bi, clipR.x, clipR.y, null);
273             } else {
274                 // Use tiles to draw image...
275
wr = Raster.createWritableRaster(srcSM, new Point JavaDoc(0,0));
276                 BufferedImage JavaDoc bi = new BufferedImage JavaDoc
277                     (srcCM, wr, srcCM.isAlphaPremultiplied(), null);
278
279                 int xt0 = cr.getMinTileX();
280                 int xt1 = xt0+cr.getNumXTiles();
281                 int yt0 = cr.getMinTileY();
282                 int yt1 = yt0+cr.getNumYTiles();
283                 int tw = srcSM.getWidth();
284                 int th = srcSM.getHeight();
285
286                 Rectangle JavaDoc tR = new Rectangle JavaDoc(0,0,tw,th);
287                 Rectangle JavaDoc iR = new Rectangle JavaDoc(0,0,0,0);
288
289                 if (false) {
290                     System.err.println("SrcCM: " + srcCM);
291                     System.err.println("CR: " + cr);
292                     System.err.println("CRR: " + crR + " TG: [" +
293                                        xt0 +"," +
294                                        yt0 +"," +
295                                        xt1 +"," +
296                                        yt1 +"] Off: " +
297                                        cr.getTileGridXOffset() +"," +
298                                        cr.getTileGridYOffset());
299                 }
300
301                 int yloc = yt0*th+cr.getTileGridYOffset();
302                 int skip = (clipR.y-yloc)/th;
303                 if (skip <0) skip = 0;
304                 yt0+=skip;
305
306                 int xloc = xt0*tw+cr.getTileGridXOffset();
307                 skip = (clipR.x-xloc)/tw;
308                 if (skip <0) skip = 0;
309                 xt0+=skip;
310
311                 int endX = clipR.x+clipR.width-1;
312                 int endY = clipR.y+clipR.height-1;
313
314                 if (false) {
315                     System.out.println("clipR: " + clipR + " TG: [" +
316                                        xt0 +"," +
317                                        yt0 +"," +
318                                        xt1 +"," +
319                                        yt1 +"] Off: " +
320                                        cr.getTileGridXOffset() +"," +
321                                        cr.getTileGridYOffset());
322                 }
323
324
325                 yloc = yt0*th+cr.getTileGridYOffset();
326                 int minX = xt0*tw+cr.getTileGridXOffset();
327                 int xStep = tw;
328                 xloc = minX;
329                 for (int y=yt0; y<yt1; y++, yloc += th) {
330                     if (yloc > endY) break;
331                     for (int x=xt0; x<xt1; x++, xloc+=xStep) {
332                         if ((xloc<minX) || (xloc > endX)) break;
333                         tR.x = xloc;
334                         tR.y = yloc;
335                         Rectangle2D.intersect(crR, tR, iR);
336
337                         WritableRaster JavaDoc twr;
338                         twr = wr.createWritableChild(0, 0,
339                                                      iR.width, iR.height,
340                                                      iR.x, iR.y, null);
341
342                         // System.out.println("Generating tile: " + twr);
343
cr.copyData(twr);
344
345                         // Make sure we only draw the region that was written.
346
BufferedImage JavaDoc subBI;
347                         subBI = bi.getSubimage(0, 0, iR.width, iR.height);
348
349                         if (false) {
350                             System.out.println("Drawing: " + tR);
351                             System.out.println("IR: " + iR);
352                         }
353
354                         // For some reason using the transform version
355
// causes a gStackUnderflow error but if I just
356
// use the drawImage with an x & y it works.
357
g2d.drawImage(subBI, iR.x, iR.y, null);
358                         // AffineTransform trans
359
// = AffineTransform.getTranslateInstance(iR.x, iR.y);
360
// g2d.drawImage(subBI, trans, null);
361

362                         // String label = "sub [" + x + ", " + y + "]: ";
363
// org.ImageDisplay.showImage
364
// (label, subBI);
365
}
366                     xStep = -xStep; // Reverse directions.
367
xloc += xStep; // Get back in bounds.
368
}
369             }
370             // long endTime = System.currentTimeMillis();
371
// System.out.println("Time: " + (endTime-startTime));
372

373
374         } finally {
375             g2d.setTransform(g2dAt);
376             g2d.setComposite(g2dComposite);
377         }
378
379         // System.out.println("Finished Draw");
380
}
381
382
383     /**
384      * Draws a <tt>Filter</tt> (<tt>RenderableImage</tt>) into a
385      * Graphics 2D after taking into account a particular
386      * <tt>RenderContext</tt>.<p>
387      *
388      * This method also attempts to unwind the rendering chain a bit.
389      * So it knows about certain operations (like affine, pad,
390      * composite), rather than applying each of these operations in
391      * turn it accounts for there affects through modifications to the
392      * Graphics2D. This avoids generating lots of intermediate images.
393      *
394      * @param g2d The Graphics to draw into.
395      * @param filter The filter to draw
396      * @param rc The render context that controls the drawing operation.
397      */

398     public static void drawImage(Graphics2D JavaDoc g2d,
399                                  RenderableImage JavaDoc filter,
400                                  RenderContext JavaDoc rc) {
401
402         AffineTransform JavaDoc origDev = g2d.getTransform();
403         Shape JavaDoc origClip = g2d.getClip();
404         RenderingHints JavaDoc origRH = g2d.getRenderingHints();
405
406         Shape JavaDoc clip = rc.getAreaOfInterest();
407         if (clip != null)
408             g2d.clip(clip);
409         g2d.transform(rc.getTransform());
410         g2d.setRenderingHints(rc.getRenderingHints());
411
412         drawImage(g2d, filter);
413
414         g2d.setTransform(origDev);
415         g2d.setClip(origClip);
416         g2d.setRenderingHints(origRH);
417     }
418
419     /**
420      * Draws a <tt>Filter</tt> (<tt>RenderableImage</tt>) into a
421      * Graphics 2D.<p>
422      *
423      * This method also attempts to unwind the rendering chain a bit.
424      * So it knows about certain operations (like affine, pad,
425      * composite), rather than applying each of these operations in
426      * turn it accounts for there affects through modifications to the
427      * Graphics2D. This avoids generating lots of intermediate images.
428      *
429      * @param g2d The Graphics to draw into.
430      * @param filter The filter to draw
431      */

432     public static void drawImage(Graphics2D JavaDoc g2d,
433                                  RenderableImage JavaDoc filter) {
434         if (filter instanceof PaintRable) {
435             PaintRable pr = (PaintRable)filter;
436             if (pr.paintRable(g2d))
437                 // paintRable succeeded so we are done...
438
return;
439         }
440
441         // Get our sources image...
442
// System.out.println("UnOpt: " + filter);
443
AffineTransform JavaDoc at = g2d.getTransform();
444         RenderedImage JavaDoc ri = filter.createRendering
445             (new RenderContext JavaDoc(at, g2d.getClip(), g2d.getRenderingHints()));
446
447         if (ri == null)
448             return;
449
450         g2d.setTransform(IDENTITY);
451         drawImage(g2d, GraphicsUtil.wrap(ri));
452         g2d.setTransform(at);
453     }
454
455     /**
456      * This is a wrapper around the system's
457      * BufferedImage.createGraphics that arranges for bi to be stored
458      * in a Rendering hint in the returned Graphics2D.
459      * This allows for accurate determination of the 'devices' size,
460      * and colorspace.
461
462      * @param bi The BufferedImage that the returned Graphics should
463      * draw into.
464      * @return A Graphics2D that draws into BufferedImage with <tt>bi</tt>
465      * stored in a rendering hint.
466      */

467     public static Graphics2D JavaDoc createGraphics(BufferedImage JavaDoc bi,
468                                             RenderingHints JavaDoc hints) {
469         Graphics2D JavaDoc g2d = bi.createGraphics();
470         if (hints != null)
471             g2d.addRenderingHints(hints);
472         g2d.setRenderingHint(RenderingHintsKeyExt.KEY_BUFFERED_IMAGE,
473                              new WeakReference JavaDoc(bi));
474         g2d.clip(new Rectangle JavaDoc(0, 0, bi.getWidth(), bi.getHeight()));
475         return g2d;
476     }
477
478
479     public static Graphics2D JavaDoc createGraphics(BufferedImage JavaDoc bi) {
480         Graphics2D JavaDoc g2d = bi.createGraphics();
481         g2d.setRenderingHint(RenderingHintsKeyExt.KEY_BUFFERED_IMAGE,
482                              new WeakReference JavaDoc(bi));
483         g2d.clip(new Rectangle JavaDoc(0, 0, bi.getWidth(), bi.getHeight()));
484         return g2d;
485     }
486
487
488     public final static boolean WARN_DESTINATION = true;
489
490     public static BufferedImage JavaDoc getDestination(Graphics2D JavaDoc g2d) {
491         Object JavaDoc o = g2d.getRenderingHint
492             (RenderingHintsKeyExt.KEY_BUFFERED_IMAGE);
493         if (o != null)
494             return (BufferedImage JavaDoc)(((Reference JavaDoc)o).get());
495
496         // Check if this is a BufferedImage G2d if so throw an error...
497
GraphicsConfiguration JavaDoc gc = g2d.getDeviceConfiguration();
498         GraphicsDevice JavaDoc gd = gc.getDevice();
499         if (WARN_DESTINATION &&
500             (gd.getType() == GraphicsDevice.TYPE_IMAGE_BUFFER) &&
501             (g2d.getRenderingHint(RenderingHintsKeyExt.KEY_TRANSCODING) !=
502                 RenderingHintsKeyExt.VALUE_TRANSCODING_PRINTING))
503             // throw new IllegalArgumentException
504
System.out.println
505                 ("Graphics2D from BufferedImage lacks BUFFERED_IMAGE hint");
506
507         return null;
508     }
509
510     public static ColorModel JavaDoc getDestinationColorModel(Graphics2D JavaDoc g2d) {
511         BufferedImage JavaDoc bi = getDestination(g2d);
512         if (bi != null)
513             return bi.getColorModel();
514
515         GraphicsConfiguration JavaDoc gc = g2d.getDeviceConfiguration();
516
517         // We are going to a BufferedImage but no hint was provided
518
// so we can't determine the destination Color Model.
519
if (gc.getDevice().getType() == GraphicsDevice.TYPE_IMAGE_BUFFER) {
520             if (g2d.getRenderingHint(RenderingHintsKeyExt.KEY_TRANSCODING) ==
521                 RenderingHintsKeyExt.VALUE_TRANSCODING_PRINTING)
522                 return sRGB_Unpre;
523
524             // System.out.println("CM: " + gc.getColorModel());
525
// System.out.println("CS: " + gc.getColorModel().getColorSpace());
526
return null;
527         }
528
529         return gc.getColorModel();
530     }
531
532     public static ColorSpace JavaDoc getDestinationColorSpace(Graphics2D JavaDoc g2d) {
533         ColorModel JavaDoc cm = getDestinationColorModel(g2d);
534         if (cm != null) return cm.getColorSpace();
535
536         return null;
537     }
538
539     public static Rectangle JavaDoc getDestinationBounds(Graphics2D JavaDoc g2d) {
540         BufferedImage JavaDoc bi = getDestination(g2d);
541         if (bi != null)
542             return new Rectangle JavaDoc(0, 0, bi.getWidth(), bi.getHeight());
543
544         GraphicsConfiguration JavaDoc gc = g2d.getDeviceConfiguration();
545
546         // We are going to a BufferedImage but no hint was provided
547
// so we can't determine the destination bounds.
548
if (gc.getDevice().getType() == GraphicsDevice.TYPE_IMAGE_BUFFER)
549             return null;
550
551         // This is a JDK 1.3ism, so we will just return null...
552
// return gc.getBounds();
553
return null;
554     }
555
556
557     /**
558      * Standard prebuilt Linear_sRGB color model with no alpha */

559     public final static ColorModel JavaDoc Linear_sRGB =
560         new DirectColorModel JavaDoc(ColorSpace.getInstance
561                              (ColorSpace.CS_LINEAR_RGB), 24,
562                              0x00FF0000, 0x0000FF00,
563                              0x000000FF, 0x0, false,
564                              DataBuffer.TYPE_INT);
565     /**
566      * Standard prebuilt Linear_sRGB color model with premultiplied alpha.
567      */

568     public final static ColorModel JavaDoc Linear_sRGB_Pre =
569         new DirectColorModel JavaDoc(ColorSpace.getInstance
570                              (ColorSpace.CS_LINEAR_RGB), 32,
571                              0x00FF0000, 0x0000FF00,
572                              0x000000FF, 0xFF000000, true,
573                              DataBuffer.TYPE_INT);
574     /**
575      * Standard prebuilt Linear_sRGB color model with unpremultiplied alpha.
576      */

577     public final static ColorModel JavaDoc Linear_sRGB_Unpre =
578         new DirectColorModel JavaDoc(ColorSpace.getInstance
579                              (ColorSpace.CS_LINEAR_RGB), 32,
580                              0x00FF0000, 0x0000FF00,
581                              0x000000FF, 0xFF000000, false,
582                              DataBuffer.TYPE_INT);
583
584     /**
585      * Standard prebuilt sRGB color model with no alpha.
586      */

587     public final static ColorModel JavaDoc sRGB =
588         new DirectColorModel JavaDoc(ColorSpace.getInstance
589                              (ColorSpace.CS_sRGB), 24,
590                              0x00FF0000, 0x0000FF00,
591                              0x000000FF, 0x0, false,
592                              DataBuffer.TYPE_INT);
593     /**
594      * Standard prebuilt sRGB color model with premultiplied alpha.
595      */

596     public final static ColorModel JavaDoc sRGB_Pre =
597         new DirectColorModel JavaDoc(ColorSpace.getInstance
598                              (ColorSpace.CS_sRGB), 32,
599                              0x00FF0000, 0x0000FF00,
600                              0x000000FF, 0xFF000000, true,
601                              DataBuffer.TYPE_INT);
602     /**
603      * Standard prebuilt sRGB color model with unpremultiplied alpha.
604      */

605     public final static ColorModel JavaDoc sRGB_Unpre =
606         new DirectColorModel JavaDoc(ColorSpace.getInstance
607                              (ColorSpace.CS_sRGB), 32,
608                              0x00FF0000, 0x0000FF00,
609                              0x000000FF, 0xFF000000, false,
610                              DataBuffer.TYPE_INT);
611
612     /**
613      * Method that returns either Linear_sRGB_Pre or Linear_sRGB_UnPre
614      * based on premult flag.
615      * @param premult True if the ColorModel should have premultiplied alpha.
616      * @return a ColorMdoel with Linear sRGB colorSpace and
617      * the alpha channel set in accordance with
618      * <tt>premult</tt>
619      */

620     public static ColorModel JavaDoc makeLinear_sRGBCM(boolean premult) {
621         if (premult)
622             return Linear_sRGB_Pre;
623         return Linear_sRGB_Unpre;
624     }
625
626     /**
627      * Constructs a BufferedImage with a linear sRGB colorModel, and alpha.
628      * @param width The desired width of the BufferedImage
629      * @param height The desired height of the BufferedImage
630      * @param premult The desired state of alpha premultiplied
631      * @return The requested BufferedImage.
632      */

633     public static BufferedImage JavaDoc makeLinearBufferedImage(int width,
634                                                         int height,
635                                                         boolean premult) {
636         ColorModel JavaDoc cm = makeLinear_sRGBCM(premult);
637         WritableRaster JavaDoc wr = cm.createCompatibleWritableRaster(width, height);
638         return new BufferedImage JavaDoc(cm, wr, premult, null);
639     }
640
641     /**
642      * This method will return a CacheableRed that has it's data in
643      * the linear sRGB colorspace. If <tt>src</tt> is already in
644      * linear sRGB then this method does nothing and returns <tt>src</tt>.
645      * Otherwise it creates a transform that will convert
646      * <tt>src</tt>'s output to linear sRGB and returns that CacheableRed.
647      *
648      * @param src The image to convert to linear sRGB.
649      * @return An equivilant image to <tt>src</tt> who's data is in
650      * linear sRGB.
651      */

652     public static CachableRed convertToLsRGB(CachableRed src) {
653         ColorModel JavaDoc cm = src.getColorModel();
654         ColorSpace JavaDoc cs = cm.getColorSpace();
655         if (cs == ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB))
656             return src;
657
658         return new Any2LsRGBRed(src);
659     }
660
661     /**
662      * This method will return a CacheableRed that has it's data in
663      * the sRGB colorspace. If <tt>src</tt> is already in
664      * sRGB then this method does nothing and returns <tt>src</tt>.
665      * Otherwise it creates a transform that will convert
666      * <tt>src</tt>'s output to sRGB and returns that CacheableRed.
667      *
668      * @param src The image to convert to sRGB.
669      * @return An equivilant image to <tt>src</tt> who's data is in sRGB.
670      */

671     public static CachableRed convertTosRGB(CachableRed src) {
672         ColorModel JavaDoc cm = src.getColorModel();
673         ColorSpace JavaDoc cs = cm.getColorSpace();
674         if (cs == ColorSpace.getInstance(ColorSpace.CS_sRGB))
675             return src;
676
677         return new Any2sRGBRed(src);
678     }
679
680     /**
681      * Convertes any RenderedImage to a CacheableRed. <p>
682      * If <tt>ri</tt> is already a CacheableRed it casts it down and
683      * returns it.<p>
684      *
685      * In cases where <tt>ri</tt> is not already a CacheableRed it
686      * wraps <tt>ri</tt> with a helper class. The wrapped
687      * CacheableRed "Pretends" that it has no sources since it has no
688      * way of inteligently handling the dependency/dirty region calls
689      * if it exposed the source.
690      * @param ri The RenderedImage to convert.
691      * @return a CacheableRed that contains the same data as ri.
692      */

693     public static CachableRed wrap(RenderedImage JavaDoc ri) {
694         if (ri instanceof CachableRed)
695             return (CachableRed) ri;
696         if (ri instanceof BufferedImage JavaDoc)
697             return new BufferedImageCachableRed((BufferedImage JavaDoc)ri);
698         return new RenderedImageCachableRed(ri);
699     }
700
701     /**
702      * An internal optimized version of copyData designed to work on
703      * Integer packed data with a SinglePixelPackedSampleModel. Only
704      * the region of overlap between src and dst is copied.
705      *
706      * Calls to this should be preflighted with is_INT_PACK_Data
707      * on both src and dest (requireAlpha can be false).
708      *
709      * @param src The source of the data
710      * @param dst The destination for the data.
711      */

712     public static void copyData_INT_PACK(Raster JavaDoc src, WritableRaster JavaDoc dst) {
713         // System.out.println("Fast copyData");
714
int x0 = dst.getMinX();
715         if (x0 < src.getMinX()) x0 = src.getMinX();
716
717         int y0 = dst.getMinY();
718         if (y0 < src.getMinY()) y0 = src.getMinY();
719
720         int x1 = dst.getMinX()+dst.getWidth()-1;
721         if (x1 > src.getMinX()+src.getWidth()-1)
722             x1 = src.getMinX()+src.getWidth()-1;
723
724         int y1 = dst.getMinY()+dst.getHeight()-1;
725         if (y1 > src.getMinY()+src.getHeight()-1)
726             y1 = src.getMinY()+src.getHeight()-1;
727
728         int width = x1-x0+1;
729         int height = y1-y0+1;
730
731         SinglePixelPackedSampleModel JavaDoc srcSPPSM;
732         srcSPPSM = (SinglePixelPackedSampleModel JavaDoc)src.getSampleModel();
733
734         final int srcScanStride = srcSPPSM.getScanlineStride();
735         DataBufferInt JavaDoc srcDB = (DataBufferInt JavaDoc)src.getDataBuffer();
736         final int [] srcPixels = srcDB.getBankData()[0];
737         final int srcBase =
738             (srcDB.getOffset() +
739              srcSPPSM.getOffset(x0-src.getSampleModelTranslateX(),
740                                 y0-src.getSampleModelTranslateY()));
741
742
743         SinglePixelPackedSampleModel JavaDoc dstSPPSM;
744         dstSPPSM = (SinglePixelPackedSampleModel JavaDoc)dst.getSampleModel();
745
746         final int dstScanStride = dstSPPSM.getScanlineStride();
747         DataBufferInt JavaDoc dstDB = (DataBufferInt JavaDoc)dst.getDataBuffer();
748         final int [] dstPixels = dstDB.getBankData()[0];
749         final int dstBase =
750             (dstDB.getOffset() +
751              dstSPPSM.getOffset(x0-dst.getSampleModelTranslateX(),
752                                 y0-dst.getSampleModelTranslateY()));
753
754         if ((srcScanStride == dstScanStride) &&
755             (srcScanStride == width)) {
756             // System.out.println("VERY Fast copyData");
757

758             System.arraycopy(srcPixels, srcBase, dstPixels, dstBase,
759                              width*height);
760         } else if (width > 128) {
761             int srcSP = srcBase;
762             int dstSP = dstBase;
763             for (int y=0; y<height; y++) {
764                 System.arraycopy(srcPixels, srcSP, dstPixels, dstSP, width);
765                 srcSP += srcScanStride;
766                 dstSP += dstScanStride;
767             }
768         } else {
769             for (int y=0; y<height; y++) {
770                 int srcSP = srcBase+y*srcScanStride;
771                 int dstSP = dstBase+y*dstScanStride;
772                 for (int x=0; x<width; x++)
773                     dstPixels[dstSP++] = srcPixels[srcSP++];
774             }
775         }
776     }
777
778     public static void copyData_FALLBACK(Raster JavaDoc src, WritableRaster JavaDoc dst) {
779         // System.out.println("Fallback copyData");
780

781         int x0 = dst.getMinX();
782         if (x0 < src.getMinX()) x0 = src.getMinX();
783
784         int y0 = dst.getMinY();
785         if (y0 < src.getMinY()) y0 = src.getMinY();
786
787         int x1 = dst.getMinX()+dst.getWidth()-1;
788         if (x1 > src.getMinX()+src.getWidth()-1)
789             x1 = src.getMinX()+src.getWidth()-1;
790
791         int y1 = dst.getMinY()+dst.getHeight()-1;
792         if (y1 > src.getMinY()+src.getHeight()-1)
793             y1 = src.getMinY()+src.getHeight()-1;
794
795         int width = x1-x0+1;
796         int [] data = null;
797
798         for (int y = y0; y <= y1 ; y++) {
799             data = src.getPixels(x0,y,width,1,data);
800             dst.setPixels (x0,y,width,1,data);
801         }
802     }
803
804     /**
805      * Copies data from one raster to another. Only the region of
806      * overlap between src and dst is copied. <tt>Src</tt> and
807      * <tt>Dst</tt> must have compatible SampleModels.
808      *
809      * @param src The source of the data
810      * @param dst The destination for the data.
811      */

812     public static void copyData(Raster JavaDoc src, WritableRaster JavaDoc dst) {
813         if (is_INT_PACK_Data(src.getSampleModel(), false) &&
814             is_INT_PACK_Data(dst.getSampleModel(), false)) {
815             copyData_INT_PACK(src, dst);
816             return;
817         }
818
819         copyData_FALLBACK(src, dst);
820     }
821
822     /**
823      * Creates a new raster that has a <b>copy</b> of the data in
824      * <tt>ras</tt>. This is highly optimized for speed. There is
825      * no provision for changing any aspect of the SampleModel.
826      *
827      * This method should be used when you need to change the contents
828      * of a Raster that you do not "own" (ie the result of a
829      * <tt>getData</tt> call).
830      * @param ras The Raster to copy.
831      * @return A writable copy of <tt>ras</tt>
832      */

833     public static WritableRaster JavaDoc copyRaster(Raster JavaDoc ras) {
834         return copyRaster(ras, ras.getMinX(), ras.getMinY());
835     }
836
837
838     /**
839      * Creates a new raster that has a <b>copy</b> of the data in
840      * <tt>ras</tt>. This is highly optimized for speed. There is
841      * no provision for changing any aspect of the SampleModel.
842      * However you can specify a new location for the returned raster.
843      *
844      * This method should be used when you need to change the contents
845      * of a Raster that you do not "own" (ie the result of a
846      * <tt>getData</tt> call).
847      *
848      * @param ras The Raster to copy.
849      *
850      * @param minX The x location for the upper left corner of the
851      * returned WritableRaster.
852      *
853      * @param minY The y location for the upper left corner of the
854      * returned WritableRaster.
855      *
856      * @return A writable copy of <tt>ras</tt>
857      */

858     public static WritableRaster JavaDoc copyRaster(Raster JavaDoc ras, int minX, int minY) {
859         WritableRaster JavaDoc ret = Raster.createWritableRaster
860             (ras.getSampleModel(),
861              new Point JavaDoc(0,0));
862         ret = ret.createWritableChild
863             (ras.getMinX()-ras.getSampleModelTranslateX(),
864              ras.getMinY()-ras.getSampleModelTranslateY(),
865              ras.getWidth(), ras.getHeight(),
866              minX, minY, null);
867
868         // Use System.arraycopy to copy the data between the two...
869
DataBuffer JavaDoc srcDB = ras.getDataBuffer();
870         DataBuffer JavaDoc retDB = ret.getDataBuffer();
871         if (srcDB.getDataType() != retDB.getDataType()) {
872             throw new IllegalArgumentException JavaDoc
873                 ("New DataBuffer doesn't match original");
874         }
875         int len = srcDB.getSize();
876         int banks = srcDB.getNumBanks();
877         int [] offsets = srcDB.getOffsets();
878         for (int b=0; b< banks; b++) {
879             switch (srcDB.getDataType()) {
880             case DataBuffer.TYPE_BYTE: {
881                 DataBufferByte JavaDoc srcDBT = (DataBufferByte JavaDoc)srcDB;
882                 DataBufferByte JavaDoc retDBT = (DataBufferByte JavaDoc)retDB;
883                 System.arraycopy(srcDBT.getData(b), offsets[b],
884                                  retDBT.getData(b), offsets[b], len);
885             }
886             case DataBuffer.TYPE_INT: {
887                 DataBufferInt JavaDoc srcDBT = (DataBufferInt JavaDoc)srcDB;
888                 DataBufferInt JavaDoc retDBT = (DataBufferInt JavaDoc)retDB;
889                 System.arraycopy(srcDBT.getData(b), offsets[b],
890                                  retDBT.getData(b), offsets[b], len);
891             }
892             case DataBuffer.TYPE_SHORT: {
893                 DataBufferShort JavaDoc srcDBT = (DataBufferShort JavaDoc)srcDB;
894                 DataBufferShort JavaDoc retDBT = (DataBufferShort JavaDoc)retDB;
895                 System.arraycopy(srcDBT.getData(b), offsets[b],
896                                  retDBT.getData(b), offsets[b], len);
897             }
898             case DataBuffer.TYPE_USHORT: {
899                 DataBufferUShort JavaDoc srcDBT = (DataBufferUShort JavaDoc)srcDB;
900                 DataBufferUShort JavaDoc retDBT = (DataBufferUShort JavaDoc)retDB;
901                 System.arraycopy(srcDBT.getData(b), offsets[b],
902                                  retDBT.getData(b), offsets[b], len);
903             }
904             }
905         }
906
907         return ret;
908     }
909
910     /**
911      * Coerces <tt>ras</tt> to be writable. The returned Raster continues to
912      * reference the DataBuffer from ras, so modifications to the returned
913      * WritableRaster will be seen in ras.<p>
914      *
915      * This method should only be used if you need a WritableRaster due to
916      * an interface (such as to construct a BufferedImage), but have no
917      * intention of modifying the contents of the returned Raster. If
918      * you have any doubt about other users of the data in <tt>ras</tt>,
919      * use copyRaster (above).
920      * @param ras The raster to make writable.
921      * @return A Writable version of ras (shares DataBuffer with
922      * <tt>ras</tt>).
923      */

924     public static WritableRaster JavaDoc makeRasterWritable(Raster JavaDoc ras) {
925         return makeRasterWritable(ras, ras.getMinX(), ras.getMinY());
926     }
927
928     /**
929      * Coerces <tt>ras</tt> to be writable. The returned Raster continues to
930      * reference the DataBuffer from ras, so modifications to the returned
931      * WritableRaster will be seen in ras.<p>
932      *
933      * You can specify a new location for the returned WritableRaster, this
934      * is especially useful for constructing BufferedImages which require
935      * the Raster to be at (0,0).
936      *
937      * This method should only be used if you need a WritableRaster due to
938      * an interface (such as to construct a BufferedImage), but have no
939      * intention of modifying the contents of the returned Raster. If
940      * you have any doubt about other users of the data in <tt>ras</tt>,
941      * use copyRaster (above).
942      *
943      * @param ras The raster to make writable.
944      *
945      * @param minX The x location for the upper left corner of the
946      * returned WritableRaster.
947      *
948      * @param minY The y location for the upper left corner of the
949      * returned WritableRaster.
950      *
951      * @return A Writable version of <tT>ras</tt> with it's upper left
952      * hand coordinate set to minX, minY (shares it's DataBuffer
953      * with <tt>ras</tt>).
954      */

955     public static WritableRaster JavaDoc makeRasterWritable(Raster JavaDoc ras,
956                                                     int minX, int minY) {
957         WritableRaster JavaDoc ret = Raster.createWritableRaster
958             (ras.getSampleModel(),
959              ras.getDataBuffer(),
960              new Point JavaDoc(0,0));
961         ret = ret.createWritableChild
962             (ras.getMinX()-ras.getSampleModelTranslateX(),
963              ras.getMinY()-ras.getSampleModelTranslateY(),
964              ras.getWidth(), ras.getHeight(),
965              minX, minY, null);
966         return ret;
967     }
968
969     /**
970      * Create a new ColorModel with it's alpha premultiplied state matching
971      * newAlphaPreMult.
972      * @param cm The ColorModel to change the alpha premult state of.
973      * @param newAlphaPreMult The new state of alpha premult.
974      * @return A new colorModel that has isAlphaPremultiplied()
975      * equal to newAlphaPreMult.
976      */

977     public static ColorModel JavaDoc
978         coerceColorModel(ColorModel JavaDoc cm, boolean newAlphaPreMult) {
979         if (cm.isAlphaPremultiplied() == newAlphaPreMult)
980             return cm;
981
982         // Easiest way to build proper colormodel for new Alpha state...
983
// Eventually this should switch on known ColorModel types and
984
// only fall back on this hack when the CM type is unknown.
985
WritableRaster JavaDoc wr = cm.createCompatibleWritableRaster(1,1);
986         return cm.coerceData(wr, newAlphaPreMult);
987     }
988
989     /**
990      * Coerces data within a bufferedImage to match newAlphaPreMult,
991      * Note that this can not change the colormodel of bi so you
992      *
993      * @param wr The raster to change the state of.
994      * @param cm The colormodel currently associated with data in wr.
995      * @param newAlphaPreMult The desired state of alpha Premult for raster.
996      * @return A new colormodel that matches newAlphaPreMult.
997      */

998     public static ColorModel JavaDoc
999         coerceData(WritableRaster JavaDoc wr, ColorModel JavaDoc cm, boolean newAlphaPreMult) {
1000
1001        // System.out.println("CoerceData: " + cm.isAlphaPremultiplied() +
1002
// " Out: " + newAlphaPreMult);
1003
if (cm.hasAlpha()== false)
1004            // Nothing to do no alpha channel
1005
return cm;
1006
1007        if (cm.isAlphaPremultiplied() == newAlphaPreMult)
1008            // nothing to do alpha state matches...
1009
return cm;
1010
1011        // System.out.println("CoerceData: " + wr.getSampleModel());
1012

1013        if (newAlphaPreMult) {
1014            multiplyAlpha(wr);
1015        } else {
1016            divideAlpha(wr);
1017        }
1018
1019        return coerceColorModel(cm, newAlphaPreMult);
1020    }
1021
1022    public static void multiplyAlpha(WritableRaster JavaDoc wr) {
1023        if (is_BYTE_COMP_Data(wr.getSampleModel()))
1024            mult_BYTE_COMP_Data(wr);
1025        else if (is_INT_PACK_Data(wr.getSampleModel(), true))
1026            mult_INT_PACK_Data(wr);
1027        else {
1028            int [] pixel = null;
1029            int bands = wr.getNumBands();
1030            float norm = 1f/255f;
1031            int x0, x1, y0, y1, a, b;
1032            float alpha;
1033            x0 = wr.getMinX();
1034            x1 = x0+wr.getWidth();
1035            y0 = wr.getMinY();
1036            y1 = y0+wr.getHeight();
1037            for (int y=y0; y<y1; y++)
1038                for (int x=x0; x<x1; x++) {
1039                    pixel = wr.getPixel(x,y,pixel);
1040                    a = pixel[bands-1];
1041                    if ((a >= 0) && (a < 255)) {
1042                        alpha = a*norm;
1043                        for (b=0; b<bands-1; b++)
1044                            pixel[b] = (int)(pixel[b]*alpha+0.5f);
1045                        wr.setPixel(x,y,pixel);
1046                    }
1047                }
1048        }
1049    }
1050    
1051    public static void divideAlpha(WritableRaster JavaDoc wr) {
1052        if (is_BYTE_COMP_Data(wr.getSampleModel()))
1053            divide_BYTE_COMP_Data(wr);
1054        else if (is_INT_PACK_Data(wr.getSampleModel(), true))
1055            divide_INT_PACK_Data(wr);
1056        else {
1057            int x0, x1, y0, y1, a, b;
1058            float ialpha;
1059            int bands = wr.getNumBands();
1060            int [] pixel = null;
1061        
1062            x0 = wr.getMinX();
1063            x1 = x0+wr.getWidth();
1064            y0 = wr.getMinY();
1065            y1 = y0+wr.getHeight();
1066            for (int y=y0; y<y1; y++)
1067                for (int x=x0; x<x1; x++) {
1068                    pixel = wr.getPixel(x,y,pixel);
1069                    a = pixel[bands-1];
1070                    if ((a > 0) && (a < 255)) {
1071                        ialpha = 255/(float)a;
1072                        for (b=0; b<bands-1; b++)
1073                            pixel[b] = (int)(pixel[b]*ialpha+0.5f);
1074                        wr.setPixel(x,y,pixel);
1075                    }
1076                }
1077        }
1078    }
1079
1080    /**
1081     * Copies data from one bufferedImage to another paying attention
1082     * to the state of AlphaPreMultiplied.
1083     *
1084     * @param src The source
1085     * @param dst The destination
1086     */

1087    public static void
1088        copyData(BufferedImage JavaDoc src, BufferedImage JavaDoc dst) {
1089        Rectangle JavaDoc srcRect = new Rectangle JavaDoc(0, 0,
1090                                          src.getWidth(), src.getHeight());
1091        copyData(src, srcRect, dst, new Point JavaDoc(0,0));
1092    }
1093
1094
1095    /**
1096     * Copies data from one bufferedImage to another paying attention
1097     * to the state of AlphaPreMultiplied.
1098     *
1099     * @param src The source
1100     * @param srcRect The Rectangle of source data to be copied
1101     * @param dst The destination
1102     * @param destP The Place for the upper left corner of srcRect in dst.
1103     */

1104    public static void
1105        copyData(BufferedImage JavaDoc src, Rectangle JavaDoc srcRect,
1106                 BufferedImage JavaDoc dst, Point JavaDoc destP) {
1107
1108       /*
1109        if (srcCS != dstCS)
1110            throw new IllegalArgumentException
1111                ("Images must be in the same ColorSpace in order "+
1112                 "to copy Data between them");
1113        */

1114        boolean srcAlpha = src.getColorModel().hasAlpha();
1115        boolean dstAlpha = dst.getColorModel().hasAlpha();
1116
1117        // System.out.println("Src has: " + srcAlpha +
1118
// " is: " + src.isAlphaPremultiplied());
1119
//
1120
// System.out.println("Dst has: " + dstAlpha +
1121
// " is: " + dst.isAlphaPremultiplied());
1122

1123        if (srcAlpha == dstAlpha)
1124            if ((srcAlpha == false) ||
1125                (src.isAlphaPremultiplied() == dst.isAlphaPremultiplied())) {
1126                // They match one another so just copy everything...
1127
copyData(src.getRaster(), dst.getRaster());
1128                return;
1129            }
1130
1131        // System.out.println("Using Slow CopyData");
1132

1133        int [] pixel = null;
1134        Raster JavaDoc srcR = src.getRaster();
1135        WritableRaster JavaDoc dstR = dst.getRaster();
1136        int bands = dstR.getNumBands();
1137
1138        int dx = destP.x-srcRect.x;
1139        int dy = destP.y-srcRect.y;
1140
1141        int w = srcRect.width;
1142        int x0 = srcRect.x;
1143        int y0 = srcRect.y;
1144        int y1 = y0+srcRect.height-1;
1145
1146        if (!srcAlpha) {
1147            // Src has no alpha dest does so set alpha to 1.0 everywhere.
1148
// System.out.println("Add Alpha");
1149
int [] oPix = new int[bands*w];
1150            int out = (w*bands)-1; // The 2 skips alpha channel
1151
while(out >= 0) {
1152                // Fill alpha channel with 255's
1153
oPix[out] = 255;
1154                out -= bands;
1155            }
1156
1157            int b, in;
1158            for (int y=y0; y<=y1; y++) {
1159                pixel = srcR.getPixels(x0,y,w,1,pixel);
1160                in = w*(bands-1)-1;
1161                out = (w*bands)-2; // The 2 skips alpha channel on last pix
1162
switch (bands) {
1163                case 4:
1164                    while(in >= 0) {
1165                        oPix[out--] = pixel[in--];
1166                        oPix[out--] = pixel[in--];
1167                        oPix[out--] = pixel[in--];
1168                        out--;
1169                    }
1170                    break;
1171                default:
1172                    while(in >= 0) {
1173                        for (b=0; b<bands-1; b++)
1174                            oPix[out--] = pixel[in--];
1175                        out--;
1176                    }
1177                }
1178                dstR.setPixels(x0+dx, y+dy, w, 1, oPix);
1179            }
1180        } else if (dstAlpha && dst.isAlphaPremultiplied()) {
1181            // Src and dest have Alpha but we need to multiply it for dst.
1182
// System.out.println("Mult Case");
1183
int a, b, alpha, in, fpNorm = (1<<24)/255, pt5 = 1<<23;
1184            for (int y=y0; y<=y1; y++) {
1185                pixel = srcR.getPixels(x0,y,w,1,pixel);
1186                in=bands*w-1;
1187                switch (bands) {
1188                case 4:
1189                    while(in >= 0) {
1190                        a = pixel[in];
1191                        if (a == 255)
1192                            in -= 4;
1193                        else {
1194                            in--;
1195                            alpha = fpNorm*a;
1196                            pixel[in] = (pixel[in]*alpha+pt5)>>>24; in--;
1197                            pixel[in] = (pixel[in]*alpha+pt5)>>>24; in--;
1198                            pixel[in] = (pixel[in]*alpha+pt5)>>>24; in--;
1199                        }
1200                    }
1201                    break;
1202                default:
1203                    while(in >= 0) {
1204                        a = pixel[in];
1205                        if (a == 255)
1206                            in -= bands;
1207                        else {
1208                            in--;
1209                            alpha = fpNorm*a;
1210                            for (b=0; b<bands-1; b++) {
1211                                pixel[in] = (pixel[in]*alpha+pt5)>>>24;
1212                                in--;
1213                            }
1214                        }
1215                    }
1216                }
1217                dstR.setPixels(x0+dx, y+dy, w, 1, pixel);
1218            }
1219        } else if (dstAlpha && !dst.isAlphaPremultiplied()) {
1220            // Src and dest have Alpha but we need to divide it out for dst.
1221
// System.out.println("Div Case");
1222
int a, b, ialpha, in, fpNorm = 0x00FF0000, pt5 = 1<<15;
1223            for (int y=y0; y<=y1; y++) {
1224                pixel = srcR.getPixels(x0,y,w,1,pixel);
1225                in=(bands*w)-1;
1226                switch(bands) {
1227                case 4:
1228                    while(in >= 0) {
1229                        a = pixel[in];
1230                        if ((a <= 0) || (a >= 255))
1231                            in -= 4;
1232                        else {
1233                            in--;
1234                            ialpha = fpNorm/a;
1235                            pixel[in] = (pixel[in]*ialpha+pt5)>>>16; in--;
1236                            pixel[in] = (pixel[in]*ialpha+pt5)>>>16; in--;
1237                            pixel[in] = (pixel[in]*ialpha+pt5)>>>16; in--;
1238                        }
1239                    }
1240                    break;
1241                default:
1242                    while(in >= 0) {
1243                        a = pixel[in];
1244                        if ((a <= 0) || (a >= 255))
1245                            in -= bands;
1246                        else {
1247                            in--;
1248                            ialpha = fpNorm/a;
1249                            for (b=0; b<bands-1; b++) {
1250                                pixel[in] = (pixel[in]*ialpha+pt5)>>>16;
1251                                in--;
1252                            }
1253                        }
1254                    }
1255                }
1256                dstR.setPixels(x0+dx, y+dy, w, 1, pixel);
1257            }
1258        } else if (src.isAlphaPremultiplied()) {
1259            int [] oPix = new int[bands*w];
1260            // Src has alpha dest does not so unpremult and store...
1261
// System.out.println("Remove Alpha, Div Case");
1262
int a, b, ialpha, in, out, fpNorm = 0x00FF0000, pt5 = 1<<15;
1263            for (int y=y0; y<=y1; y++) {
1264                pixel = srcR.getPixels(x0,y,w,1,pixel);
1265                in = (bands+1)*w -1;
1266                out = (bands*w)-1;
1267                while(in >= 0) {
1268                    a = pixel[in]; in--;
1269                    if (a > 0) {
1270                        if (a < 255) {
1271                            ialpha = fpNorm/a;
1272                            for (b=0; b<bands; b++)
1273                                oPix[out--] = (pixel[in--]*ialpha+pt5)>>>16;
1274                        } else
1275                            for (b=0; b<bands; b++)
1276                                oPix[out--] = pixel[in--];
1277                    } else {
1278                        in -= bands;
1279                        for (b=0; b<bands; b++)
1280                            oPix[out--] = 255;
1281                    }
1282                }
1283                dstR.setPixels(x0+dx, y+dy, w, 1, oPix);
1284            }
1285        } else {
1286            // Src has unpremult alpha, dest does not have alpha,
1287
// just copy the color channels over.
1288
Rectangle JavaDoc dstRect = new Rectangle JavaDoc(destP.x, destP.y,
1289                                              srcRect.width, srcRect.height);
1290            for (int b=0; b<bands; b++)
1291                copyBand(srcR, srcRect, b,
1292                         dstR, dstRect, b);
1293        }
1294    }
1295
1296    public static void copyBand(Raster JavaDoc src, int srcBand,
1297                                WritableRaster JavaDoc dst, int dstBand) {
1298
1299        Rectangle JavaDoc sR = src.getBounds();
1300        Rectangle JavaDoc dR = dst.getBounds();
1301        Rectangle JavaDoc cpR = sR.intersection(dR);
1302
1303        copyBand(src, cpR, srcBand, dst, cpR, dstBand);
1304    }
1305
1306    public static void copyBand(Raster JavaDoc src, Rectangle JavaDoc sR, int sBand,
1307                                WritableRaster JavaDoc dst, Rectangle JavaDoc dR, int dBand) {
1308        int dy = dR.y -sR.y;
1309        int dx = dR.x -sR.x;
1310        sR = sR.intersection(src.getBounds());
1311        dR = dR.intersection(dst.getBounds());
1312        int width, height;
1313        if (dR.width < sR.width) width = dR.width;
1314        else width = sR.width;
1315        if (dR.height < sR.height) height = dR.height;
1316        else height = sR.height;
1317
1318        int x = sR.x+dx;
1319        int [] samples = null;
1320        for (int y=sR.y; y< sR.y+height; y++) {
1321            samples = src.getSamples(sR.x, y, width, 1, sBand, samples);
1322            dst.setSamples(x, y+dy, width, 1, dBand, samples);
1323        }
1324    }
1325
1326    public static boolean is_INT_PACK_Data(SampleModel JavaDoc sm,
1327                                           boolean requireAlpha) {
1328        // Check ColorModel is of type DirectColorModel
1329
if(!(sm instanceof SinglePixelPackedSampleModel JavaDoc)) return false;
1330
1331        // Check transfer type
1332
if(sm.getDataType() != DataBuffer.TYPE_INT) return false;
1333
1334        SinglePixelPackedSampleModel JavaDoc sppsm;
1335        sppsm = (SinglePixelPackedSampleModel JavaDoc)sm;
1336
1337        int [] masks = sppsm.getBitMasks();
1338        if (masks.length == 3) {
1339            if (requireAlpha) return false;
1340        } else if (masks.length != 4)
1341            return false;
1342
1343        if(masks[0] != 0x00ff0000) return false;
1344        if(masks[1] != 0x0000ff00) return false;
1345        if(masks[2] != 0x000000ff) return false;
1346        if ((masks.length == 4) &&
1347            (masks[3] != 0xff000000)) return false;
1348
1349        return true;
1350    }
1351
1352        public static boolean is_BYTE_COMP_Data(SampleModel JavaDoc sm) {
1353            // Check ColorModel is of type DirectColorModel
1354
if(!(sm instanceof ComponentSampleModel JavaDoc)) return false;
1355
1356            // Check transfer type
1357
if(sm.getDataType() != DataBuffer.TYPE_BYTE) return false;
1358
1359            return true;
1360        }
1361
1362    protected static void divide_INT_PACK_Data(WritableRaster JavaDoc wr) {
1363        // System.out.println("Divide Int");
1364

1365        SinglePixelPackedSampleModel JavaDoc sppsm;
1366        sppsm = (SinglePixelPackedSampleModel JavaDoc)wr.getSampleModel();
1367
1368        final int width = wr.getWidth();
1369
1370        final int scanStride = sppsm.getScanlineStride();
1371        DataBufferInt JavaDoc db = (DataBufferInt JavaDoc)wr.getDataBuffer();
1372        final int base
1373            = (db.getOffset() +
1374               sppsm.getOffset(wr.getMinX()-wr.getSampleModelTranslateX(),
1375                               wr.getMinY()-wr.getSampleModelTranslateY()));
1376        int pixel, a, aFP;
1377        // Access the pixel data array
1378
final int pixels[] = db.getBankData()[0];
1379        for (int y=0; y<wr.getHeight(); y++) {
1380            int sp = base + y*scanStride;
1381            final int end = sp + width;
1382            while (sp < end) {
1383                pixel = pixels[sp];
1384                a = pixel>>>24;
1385                if (a<=0) {
1386                    pixels[sp] = 0x00FFFFFF;
1387                }
1388                else if (a<255) {
1389                    aFP = (0x00FF0000/a);
1390                    pixels[sp] =
1391                        ((a << 24) |
1392                         (((((pixel&0xFF0000)>>16)*aFP)&0xFF0000) ) |
1393                         (((((pixel&0x00FF00)>>8) *aFP)&0xFF0000)>>8 ) |
1394                         (((((pixel&0x0000FF)) *aFP)&0xFF0000)>>16));
1395                }
1396                sp++;
1397            }
1398        }
1399    }
1400
1401    protected static void mult_INT_PACK_Data(WritableRaster JavaDoc wr) {
1402        // System.out.println("Multiply Int: " + wr);
1403

1404        SinglePixelPackedSampleModel JavaDoc sppsm;
1405        sppsm = (SinglePixelPackedSampleModel JavaDoc)wr.getSampleModel();
1406
1407        final int width = wr.getWidth();
1408
1409        final int scanStride = sppsm.getScanlineStride();
1410        DataBufferInt JavaDoc db = (DataBufferInt JavaDoc)wr.getDataBuffer();
1411        final int base
1412            = (db.getOffset() +
1413               sppsm.getOffset(wr.getMinX()-wr.getSampleModelTranslateX(),
1414                               wr.getMinY()-wr.getSampleModelTranslateY()));
1415        // Access the pixel data array
1416
final int pixels[] = db.getBankData()[0];
1417        for (int y=0; y<wr.getHeight(); y++) {
1418            int sp = base + y*scanStride;
1419            final int end = sp + width;
1420            while (sp < end) {
1421                int pixel = pixels[sp];
1422                int a = pixel>>>24;
1423                if ((a>=0) && (a<255)) {
1424                    pixels[sp] = ((a << 24) |
1425                                  ((((pixel&0xFF0000)*a)>>8)&0xFF0000) |
1426                                  ((((pixel&0x00FF00)*a)>>8)&0x00FF00) |
1427                                  ((((pixel&0x0000FF)*a)>>8)&0x0000FF));
1428                }
1429                sp++;
1430            }
1431        }
1432    }
1433
1434
1435    protected static void divide_BYTE_COMP_Data(WritableRaster JavaDoc wr) {
1436        // System.out.println("Multiply Int: " + wr);
1437

1438        ComponentSampleModel JavaDoc csm;
1439        csm = (ComponentSampleModel JavaDoc)wr.getSampleModel();
1440
1441        final int width = wr.getWidth();
1442
1443        final int scanStride = csm.getScanlineStride();
1444        final int pixStride = csm.getPixelStride();
1445        final int [] bandOff = csm.getBandOffsets();
1446
1447        DataBufferByte JavaDoc db = (DataBufferByte JavaDoc)wr.getDataBuffer();
1448        final int base
1449            = (db.getOffset() +
1450               csm.getOffset(wr.getMinX()-wr.getSampleModelTranslateX(),
1451                             wr.getMinY()-wr.getSampleModelTranslateY()));
1452
1453
1454        int a=0;
1455        int aOff = bandOff[bandOff.length-1];
1456        int bands = bandOff.length-1;
1457        int b, i;
1458        // Access the pixel data array
1459
final byte pixels[] = db.getBankData()[0];
1460        for (int y=0; y<wr.getHeight(); y++) {
1461            int sp = base + y*scanStride;
1462            final int end = sp + width*pixStride;
1463            while (sp < end) {
1464              a = pixels[sp+aOff]&0xFF;
1465              if (a==0) {
1466                for (b=0; b<bands; b++)
1467                  pixels[sp+bandOff[b]] = (byte)0xFF;
1468              } else if (a<255) {
1469                int aFP = (0x00FF0000/a);
1470                for (b=0; b<bands; b++) {
1471                  i = sp+bandOff[b];
1472                  pixels[i] = (byte)(((pixels[i]&0xFF)*aFP)>>>16);
1473                }
1474              }
1475              sp+=pixStride;
1476            }
1477        }
1478    }
1479
1480    protected static void mult_BYTE_COMP_Data(WritableRaster JavaDoc wr) {
1481        // System.out.println("Multiply Int: " + wr);
1482

1483        ComponentSampleModel JavaDoc csm;
1484        csm = (ComponentSampleModel JavaDoc)wr.getSampleModel();
1485
1486        final int width = wr.getWidth();
1487
1488        final int scanStride = csm.getScanlineStride();
1489        final int pixStride = csm.getPixelStride();
1490        final int [] bandOff = csm.getBandOffsets();
1491
1492        DataBufferByte JavaDoc db = (DataBufferByte JavaDoc)wr.getDataBuffer();
1493        final int base
1494            = (db.getOffset() +
1495               csm.getOffset(wr.getMinX()-wr.getSampleModelTranslateX(),
1496                             wr.getMinY()-wr.getSampleModelTranslateY()));
1497
1498
1499        int a=0;
1500        int aOff = bandOff[bandOff.length-1];
1501        int bands = bandOff.length-1;
1502        int b, i;
1503
1504        // Access the pixel data array
1505
final byte pixels[] = db.getBankData()[0];
1506        for (int y=0; y<wr.getHeight(); y++) {
1507            int sp = base + y*scanStride;
1508            final int end = sp + width*pixStride;
1509            while (sp < end) {
1510              a = pixels[sp+aOff]&0xFF;
1511              if (a!=0xFF)
1512                for (b=0; b<bands; b++) {
1513                  i = sp+bandOff[b];
1514                  pixels[i] = (byte)(((pixels[i]&0xFF)*a)>>8);
1515                }
1516              sp+=pixStride;
1517            }
1518        }
1519    }
1520
1521/*
1522  This is skanky debugging code that might be useful in the future:
1523
1524            if (count == 33) {
1525                String label = "sub [" + x + ", " + y + "]: ";
1526                org.ImageDisplay.showImage
1527                    (label, subBI);
1528                org.ImageDisplay.printImage
1529                    (label, subBI,
1530                     new Rectangle(75-iR.x, 90-iR.y, 32, 32));
1531                
1532            }
1533
1534
1535            // if ((count++ % 50) == 10)
1536            // org.ImageDisplay.showImage("foo: ", subBI);
1537
1538
1539            Graphics2D realG2D = g2d;
1540            while (realG2D instanceof sun.java2d.ProxyGraphics2D) {
1541                realG2D = ((sun.java2d.ProxyGraphics2D)realG2D).getDelegate();
1542            }
1543            if (realG2D instanceof sun.awt.image.BufferedImageGraphics2D) {
1544                count++;
1545                if (count == 34) {
1546                    RenderedImage ri;
1547                    ri = ((sun.awt.image.BufferedImageGraphics2D)realG2D).bufImg;
1548                    // g2d.setComposite(SVGComposite.OVER);
1549                    // org.ImageDisplay.showImage("Bar: " + count, cr);
1550                    org.ImageDisplay.printImage("Bar: " + count, cr,
1551                                                new Rectangle(75, 90, 32, 32));
1552
1553                    org.ImageDisplay.showImage ("Foo: " + count, ri);
1554                    org.ImageDisplay.printImage("Foo: " + count, ri,
1555                                                new Rectangle(75, 90, 32, 32));
1556
1557                    System.out.println("BI: " + ri);
1558                    System.out.println("BISM: " + ri.getSampleModel());
1559                    System.out.println("BICM: " + ri.getColorModel());
1560                    System.out.println("BICM class: " + ri.getColorModel().getClass());
1561                    System.out.println("BICS: " + ri.getColorModel().getColorSpace());
1562                    System.out.println
1563                        ("sRGB CS: " +
1564                         ColorSpace.getInstance(ColorSpace.CS_sRGB));
1565                    System.out.println("G2D info");
1566                    System.out.println("\tComposite: " + g2d.getComposite());
1567                    System.out.println("\tTransform" + g2d.getTransform());
1568                    java.awt.RenderingHints rh = g2d.getRenderingHints();
1569                    java.util.Set keys = rh.keySet();
1570                    java.util.Iterator iter = keys.iterator();
1571                    while (iter.hasNext()) {
1572                        Object o = iter.next();
1573
1574                        System.out.println("\t" + o.toString() + " -> " +
1575                                           rh.get(o).toString());
1576                    }
1577
1578                    ri = cr;
1579                    System.out.println("RI: " + ri);
1580                    System.out.println("RISM: " + ri.getSampleModel());
1581                    System.out.println("RICM: " + ri.getColorModel());
1582                    System.out.println("RICM class: " + ri.getColorModel().getClass());
1583                    System.out.println("RICS: " + ri.getColorModel().getColorSpace());
1584                }
1585            }
1586*/

1587
1588}
1589
Popular Tags