001/***************************************************************************** 002 * Copyright (C) The Apache Software Foundation. All rights reserved. * 003 * ------------------------------------------------------------------------- * 004 * This software is published under the terms of the Apache Software License * 005 * version 1.1, a copy of which has been included with this distribution in * 006 * the LICENSE file. * 007 *****************************************************************************/ 008 009package com.kitfox.svg.batik; 010 011import java.awt.Color; 012import java.awt.Composite; 013import java.awt.Graphics2D; 014import java.awt.GraphicsConfiguration; 015import java.awt.GraphicsDevice; 016import java.awt.Point; 017import java.awt.Rectangle; 018import java.awt.RenderingHints; 019import java.awt.Shape; 020import java.awt.color.ColorSpace; 021import java.awt.geom.AffineTransform; 022import java.awt.geom.Rectangle2D; 023import java.awt.image.BufferedImage; 024import java.awt.image.ColorModel; 025import java.awt.image.ComponentSampleModel; 026import java.awt.image.DataBuffer; 027import java.awt.image.DataBufferByte; 028import java.awt.image.DataBufferInt; 029import java.awt.image.DataBufferShort; 030import java.awt.image.DataBufferUShort; 031import java.awt.image.DirectColorModel; 032import java.awt.image.Raster; 033import java.awt.image.RenderedImage; 034import java.awt.image.SampleModel; 035import java.awt.image.SinglePixelPackedSampleModel; 036import java.awt.image.WritableRaster; 037import java.awt.image.renderable.RenderContext; 038import java.awt.image.renderable.RenderableImage; 039import java.lang.ref.Reference; 040import java.lang.ref.WeakReference; 041 042/** 043 * 044 * @author kitfox 045 */ 046public class GraphicsUtil 047{ 048 049 /** Creates a new instance of GraphicsUtil */ 050 public GraphicsUtil() 051 { 052 } 053 054 /** 055 * Create a new ColorModel with it's alpha premultiplied state matching 056 * newAlphaPreMult. 057 * @param cm The ColorModel to change the alpha premult state of. 058 * @param newAlphaPreMult The new state of alpha premult. 059 * @return A new colorModel that has isAlphaPremultiplied() 060 * equal to newAlphaPreMult. 061 */ 062 public static ColorModel coerceColorModel(ColorModel cm, boolean newAlphaPreMult) 063 { 064 if (cm.isAlphaPremultiplied() == newAlphaPreMult) 065 return cm; 066 067 // Easiest way to build proper colormodel for new Alpha state... 068 // Eventually this should switch on known ColorModel types and 069 // only fall back on this hack when the CM type is unknown. 070 WritableRaster wr = cm.createCompatibleWritableRaster(1,1); 071 return cm.coerceData(wr, newAlphaPreMult); 072 } 073 074 /** 075 * Coerces data within a bufferedImage to match newAlphaPreMult, 076 * Note that this can not change the colormodel of bi so you 077 * 078 * @param wr The raster to change the state of. 079 * @param cm The colormodel currently associated with data in wr. 080 * @param newAlphaPreMult The desired state of alpha Premult for raster. 081 * @return A new colormodel that matches newAlphaPreMult. 082 */ 083 public static ColorModel coerceData(WritableRaster wr, ColorModel cm, boolean newAlphaPreMult) 084 { 085 086 // System.out.println("CoerceData: " + cm.isAlphaPremultiplied() + 087 // " Out: " + newAlphaPreMult); 088 if (cm.hasAlpha()== false) 089 // Nothing to do no alpha channel 090 return cm; 091 092 if (cm.isAlphaPremultiplied() == newAlphaPreMult) 093 // nothing to do alpha state matches... 094 return cm; 095 096 // System.out.println("CoerceData: " + wr.getSampleModel()); 097 098 int [] pixel = null; 099 int bands = wr.getNumBands(); 100 float norm; 101 if (newAlphaPreMult) 102 { 103 if (is_BYTE_COMP_Data(wr.getSampleModel())) 104 mult_BYTE_COMP_Data(wr); 105 else if (is_INT_PACK_Data(wr.getSampleModel(), true)) 106 mult_INT_PACK_Data(wr); 107 else 108 { 109 norm = 1f/255f; 110 int x0, x1, y0, y1, a, b; 111 float alpha; 112 x0 = wr.getMinX(); 113 x1 = x0+wr.getWidth(); 114 y0 = wr.getMinY(); 115 y1 = y0+wr.getHeight(); 116 for (int y=y0; y<y1; y++) 117 for (int x=x0; x<x1; x++) 118 { 119 pixel = wr.getPixel(x,y,pixel); 120 a = pixel[bands-1]; 121 if ((a >= 0) && (a < 255)) 122 { 123 alpha = a*norm; 124 for (b=0; b<bands-1; b++) 125 pixel[b] = (int)(pixel[b]*alpha+0.5f); 126 wr.setPixel(x,y,pixel); 127 } 128 } 129 } 130 } else 131 { 132 if (is_BYTE_COMP_Data(wr.getSampleModel())) 133 divide_BYTE_COMP_Data(wr); 134 else if (is_INT_PACK_Data(wr.getSampleModel(), true)) 135 divide_INT_PACK_Data(wr); 136 else 137 { 138 int x0, x1, y0, y1, a, b; 139 float ialpha; 140 x0 = wr.getMinX(); 141 x1 = x0+wr.getWidth(); 142 y0 = wr.getMinY(); 143 y1 = y0+wr.getHeight(); 144 for (int y=y0; y<y1; y++) 145 for (int x=x0; x<x1; x++) 146 { 147 pixel = wr.getPixel(x,y,pixel); 148 a = pixel[bands-1]; 149 if ((a > 0) && (a < 255)) 150 { 151 ialpha = 255/(float)a; 152 for (b=0; b<bands-1; b++) 153 pixel[b] = (int)(pixel[b]*ialpha+0.5f); 154 wr.setPixel(x,y,pixel); 155 } 156 } 157 } 158 } 159 160 return coerceColorModel(cm, newAlphaPreMult); 161 } 162 163 164 public static boolean is_INT_PACK_Data(SampleModel sm, 165 boolean requireAlpha) 166 { 167 // Check ColorModel is of type DirectColorModel 168 if(!(sm instanceof SinglePixelPackedSampleModel)) return false; 169 170 // Check transfer type 171 if(sm.getDataType() != DataBuffer.TYPE_INT) return false; 172 173 SinglePixelPackedSampleModel sppsm; 174 sppsm = (SinglePixelPackedSampleModel)sm; 175 176 int [] masks = sppsm.getBitMasks(); 177 if (masks.length == 3) 178 { 179 if (requireAlpha) return false; 180 } else if (masks.length != 4) 181 return false; 182 183 if(masks[0] != 0x00ff0000) return false; 184 if(masks[1] != 0x0000ff00) return false; 185 if(masks[2] != 0x000000ff) return false; 186 if ((masks.length == 4) && 187 (masks[3] != 0xff000000)) return false; 188 189 return true; 190 } 191 192 protected static void mult_INT_PACK_Data(WritableRaster wr) 193 { 194 // System.out.println("Multiply Int: " + wr); 195 196 SinglePixelPackedSampleModel sppsm; 197 sppsm = (SinglePixelPackedSampleModel)wr.getSampleModel(); 198 199 final int width = wr.getWidth(); 200 201 final int scanStride = sppsm.getScanlineStride(); 202 DataBufferInt db = (DataBufferInt)wr.getDataBuffer(); 203 final int base 204 = (db.getOffset() + 205 sppsm.getOffset(wr.getMinX()-wr.getSampleModelTranslateX(), 206 wr.getMinY()-wr.getSampleModelTranslateY())); 207 int n=0; 208 // Access the pixel data array 209 final int pixels[] = db.getBankData()[0]; 210 for (int y=0; y<wr.getHeight(); y++) 211 { 212 int sp = base + y*scanStride; 213 final int end = sp + width; 214 while (sp < end) 215 { 216 int pixel = pixels[sp]; 217 int a = pixel>>>24; 218 if ((a>=0) && (a<255)) 219 { 220 pixels[sp] = ((a << 24) | 221 ((((pixel&0xFF0000)*a)>>8)&0xFF0000) | 222 ((((pixel&0x00FF00)*a)>>8)&0x00FF00) | 223 ((((pixel&0x0000FF)*a)>>8)&0x0000FF)); 224 } 225 sp++; 226 } 227 } 228 } 229 230 protected static void divide_INT_PACK_Data(WritableRaster wr) 231 { 232 // System.out.println("Divide Int"); 233 234 SinglePixelPackedSampleModel sppsm; 235 sppsm = (SinglePixelPackedSampleModel)wr.getSampleModel(); 236 237 final int width = wr.getWidth(); 238 239 final int scanStride = sppsm.getScanlineStride(); 240 DataBufferInt db = (DataBufferInt)wr.getDataBuffer(); 241 final int base 242 = (db.getOffset() + 243 sppsm.getOffset(wr.getMinX()-wr.getSampleModelTranslateX(), 244 wr.getMinY()-wr.getSampleModelTranslateY())); 245 int pixel, a, aFP, n=0; 246 // Access the pixel data array 247 final int pixels[] = db.getBankData()[0]; 248 for (int y=0; y<wr.getHeight(); y++) 249 { 250 int sp = base + y*scanStride; 251 final int end = sp + width; 252 while (sp < end) 253 { 254 pixel = pixels[sp]; 255 a = pixel>>>24; 256 if (a<=0) 257 { 258 pixels[sp] = 0x00FFFFFF; 259 } 260 else if (a<255) 261 { 262 aFP = (0x00FF0000/a); 263 pixels[sp] = 264 ((a << 24) | 265 (((((pixel&0xFF0000)>>16)*aFP)&0xFF0000) ) | 266 (((((pixel&0x00FF00)>>8) *aFP)&0xFF0000)>>8 ) | 267 (((((pixel&0x0000FF)) *aFP)&0xFF0000)>>16)); 268 } 269 sp++; 270 } 271 } 272 } 273 274 public static boolean is_BYTE_COMP_Data(SampleModel sm) 275 { 276 // Check ColorModel is of type DirectColorModel 277 if(!(sm instanceof ComponentSampleModel)) return false; 278 279 // Check transfer type 280 if(sm.getDataType() != DataBuffer.TYPE_BYTE) return false; 281 282 return true; 283 } 284 285 protected static void mult_BYTE_COMP_Data(WritableRaster wr) 286 { 287 // System.out.println("Multiply Int: " + wr); 288 289 ComponentSampleModel csm; 290 csm = (ComponentSampleModel)wr.getSampleModel(); 291 292 final int width = wr.getWidth(); 293 294 final int scanStride = csm.getScanlineStride(); 295 final int pixStride = csm.getPixelStride(); 296 final int [] bandOff = csm.getBandOffsets(); 297 298 DataBufferByte db = (DataBufferByte)wr.getDataBuffer(); 299 final int base 300 = (db.getOffset() + 301 csm.getOffset(wr.getMinX()-wr.getSampleModelTranslateX(), 302 wr.getMinY()-wr.getSampleModelTranslateY())); 303 304 305 int a=0; 306 int aOff = bandOff[bandOff.length-1]; 307 int bands = bandOff.length-1; 308 int b, i; 309 310 // Access the pixel data array 311 final byte pixels[] = db.getBankData()[0]; 312 for (int y=0; y<wr.getHeight(); y++) 313 { 314 int sp = base + y*scanStride; 315 final int end = sp + width*pixStride; 316 while (sp < end) 317 { 318 a = pixels[sp+aOff]&0xFF; 319 if (a!=0xFF) 320 for (b=0; b<bands; b++) 321 { 322 i = sp+bandOff[b]; 323 pixels[i] = (byte)(((pixels[i]&0xFF)*a)>>8); 324 } 325 sp+=pixStride; 326 } 327 } 328 } 329 330 protected static void divide_BYTE_COMP_Data(WritableRaster wr) 331 { 332 // System.out.println("Multiply Int: " + wr); 333 334 ComponentSampleModel csm; 335 csm = (ComponentSampleModel)wr.getSampleModel(); 336 337 final int width = wr.getWidth(); 338 339 final int scanStride = csm.getScanlineStride(); 340 final int pixStride = csm.getPixelStride(); 341 final int [] bandOff = csm.getBandOffsets(); 342 343 DataBufferByte db = (DataBufferByte)wr.getDataBuffer(); 344 final int base 345 = (db.getOffset() + 346 csm.getOffset(wr.getMinX()-wr.getSampleModelTranslateX(), 347 wr.getMinY()-wr.getSampleModelTranslateY())); 348 349 350 int a=0; 351 int aOff = bandOff[bandOff.length-1]; 352 int bands = bandOff.length-1; 353 int b, i; 354 // Access the pixel data array 355 final byte pixels[] = db.getBankData()[0]; 356 for (int y=0; y<wr.getHeight(); y++) 357 { 358 int sp = base + y*scanStride; 359 final int end = sp + width*pixStride; 360 while (sp < end) 361 { 362 a = pixels[sp+aOff]&0xFF; 363 if (a==0) 364 { 365 for (b=0; b<bands; b++) 366 pixels[sp+bandOff[b]] = (byte)0xFF; 367 } else if (a<255) 368 { 369 int aFP = (0x00FF0000/a); 370 for (b=0; b<bands; b++) 371 { 372 i = sp+bandOff[b]; 373 pixels[i] = (byte)(((pixels[i]&0xFF)*aFP)>>>16); 374 } 375 } 376 sp+=pixStride; 377 } 378 } 379 } 380 381 382}