1 /* 2 3 Licensed to the Apache Software Foundation (ASF) under one or more 4 contributor license agreements. See the NOTICE file distributed with 5 this work for additional information regarding copyright ownership. 6 The ASF licenses this file to You under the Apache License, Version 2.0 7 (the "License"); you may not use this file except in compliance with 8 the License. You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 See the License for the specific language governing permissions and 16 limitations under the License. 17 18 */ 19 package ch.randelshofer.quaqua.ext.batik.ext.awt.image.rendered; 20 21 import java.awt.Rectangle; 22 import java.awt.RenderingHints; 23 import java.awt.image.DataBufferInt; 24 import java.awt.image.SampleModel; 25 import java.awt.image.SinglePixelPackedSampleModel; 26 import java.awt.image.WritableRaster; 27 28 import ch.randelshofer.quaqua.ext.batik.ext.awt.image.GraphicsUtil; 29 import ch.randelshofer.quaqua.ext.batik.ext.awt.image.PadMode; 30 31 32 /** 33 * This is an implementation of a Pad operation as a RenderedImage. 34 * 35 * @author <a href="mailto:Thomas.DeWeeese@Kodak.com">Thomas DeWeese</a> 36 * @version $Id: PadRed.java 478276 2006-11-22 18:33:37Z dvholten $ */ 37 public class PadRed extends AbstractRed { 38 39 static final boolean DEBUG=false; 40 41 PadMode padMode; 42 RenderingHints hints; 43 44 /** 45 * Construct A Rendered Pad operation. If the pad is smaller than 46 * the original image size then this devolves to a Crop. 47 * 48 * @param src The image to pad/crop 49 * @param bounds The bounds of the result (same coord system as src). 50 * @param padMode The pad mode to use (currently ignored). 51 * @param hints The hints to use for drawing 'pad' area. 52 */ PadRed(CachableRed src, Rectangle bounds, PadMode padMode, RenderingHints hints)53 public PadRed(CachableRed src, 54 Rectangle bounds, 55 PadMode padMode, 56 RenderingHints hints) { 57 super(src,bounds,src.getColorModel(), 58 fixSampleModel(src, bounds), 59 bounds.x, bounds.y, 60 null); 61 62 this.padMode = padMode; 63 64 if (DEBUG) { 65 System.out.println("Src: " + src + " Bounds: " + bounds + 66 " Off: " + 67 src.getTileGridXOffset() + ", " + 68 src.getTileGridYOffset()); 69 } 70 this.hints = hints; 71 72 } 73 copyData(WritableRaster wr)74 public WritableRaster copyData(WritableRaster wr) { 75 // Get my source. 76 CachableRed src = (CachableRed)getSources().get(0); 77 78 Rectangle srcR = src.getBounds(); 79 Rectangle wrR = wr.getBounds(); 80 81 if (wrR.intersects(srcR)) { 82 Rectangle r = wrR.intersection(srcR); 83 84 // Limit the raster I send to my source to his rect. 85 WritableRaster srcWR; 86 srcWR = wr.createWritableChild(r.x, r.y, r.width, r.height, 87 r.x, r.y, null); 88 src.copyData(srcWR); 89 } 90 91 if (padMode == PadMode.ZERO_PAD) { 92 handleZero(wr); 93 } else if (padMode == PadMode.REPLICATE) { 94 handleReplicate(wr); 95 } else if (padMode == PadMode.WRAP) { 96 handleWrap(wr); 97 } 98 99 return wr; 100 } 101 102 protected static class ZeroRecter { 103 WritableRaster wr; 104 int bands; 105 static int [] zeros=null; ZeroRecter(WritableRaster wr)106 public ZeroRecter(WritableRaster wr) { 107 this.wr = wr; 108 this.bands = wr.getSampleModel().getNumBands(); 109 } zeroRect(Rectangle r)110 public void zeroRect(Rectangle r) { 111 synchronized (this) { 112 if ((zeros == null) || (zeros.length <r.width*bands)) 113 zeros = new int[r.width*bands]; 114 } 115 116 for (int y=0; y<r.height; y++) { 117 wr.setPixels(r.x, r.y+y, r.width, 1, zeros); 118 } 119 } 120 getZeroRecter(WritableRaster wr)121 public static ZeroRecter getZeroRecter(WritableRaster wr) { 122 if (GraphicsUtil.is_INT_PACK_Data(wr.getSampleModel(), false)) 123 return new ZeroRecter_INT_PACK(wr); 124 else 125 return new ZeroRecter(wr); 126 } 127 zeroRect(WritableRaster wr)128 public static void zeroRect(WritableRaster wr) { 129 ZeroRecter zr = getZeroRecter(wr); 130 zr.zeroRect(wr.getBounds()); 131 } 132 133 } 134 135 protected static class ZeroRecter_INT_PACK extends ZeroRecter { 136 final int base; 137 final int scanStride; 138 final int[] pixels; 139 final int[] zeros; 140 final int x0, y0; 141 ZeroRecter_INT_PACK(WritableRaster wr)142 public ZeroRecter_INT_PACK(WritableRaster wr) { 143 super(wr); 144 145 SinglePixelPackedSampleModel sppsm; 146 sppsm = (SinglePixelPackedSampleModel)wr.getSampleModel(); 147 148 scanStride = sppsm.getScanlineStride(); 149 DataBufferInt db = (DataBufferInt)wr.getDataBuffer(); 150 x0 = wr.getMinY(); 151 y0 = wr.getMinX(); 152 base = (db.getOffset() + 153 sppsm.getOffset(x0-wr.getSampleModelTranslateX(), 154 y0-wr.getSampleModelTranslateY())); 155 156 pixels = db.getBankData()[0]; 157 if (wr.getWidth() > 10) 158 zeros = new int[wr.getWidth()]; 159 else 160 zeros = null; 161 } 162 zeroRect(Rectangle r)163 public void zeroRect(Rectangle r) { 164 final int rbase = base+(r.x-x0) + (r.y-y0)*scanStride; 165 166 if (r.width > 10) { 167 // Longer runs use arraycopy... 168 for (int y=0; y<r.height; y++) { 169 int sp = rbase + y*scanStride; 170 System.arraycopy(zeros, 0, pixels, sp, r.width); 171 } 172 } else { 173 // Small runs quicker to avoid func call. 174 int sp = rbase; 175 int end = sp +r.width; 176 int adj = scanStride-r.width; 177 for (int y=0; y<r.height; y++) { 178 while (sp < end) 179 pixels[sp++] = 0; 180 sp += adj; 181 end += scanStride; 182 } 183 } 184 } 185 } 186 handleZero(WritableRaster wr)187 protected void handleZero(WritableRaster wr) { 188 // Get my source. 189 CachableRed src = (CachableRed)getSources().get(0); 190 Rectangle srcR = src.getBounds(); 191 Rectangle wrR = wr.getBounds(); 192 193 ZeroRecter zr = ZeroRecter.getZeroRecter(wr); 194 195 // area rect (covers the area left to handle). 196 Rectangle ar = new Rectangle(wrR.x, wrR.y, wrR.width, wrR.height); 197 // draw rect (used for calls to zeroRect); 198 Rectangle dr = new Rectangle(wrR.x, wrR.y, wrR.width, wrR.height); 199 200 // We split the edge drawing up into four parts. 201 // 202 // +-----------------------------+ 203 // | 1 | 2 | 204 // | +---------------+------| 205 // / / /4 / 206 // / / / / 207 // / / / / 208 // / / / / 209 // | +---------------+------| 210 // | | 3 | 211 // +-----------------------------+ 212 // 213 // We update our x,y, width, height as we go along so 214 // we 'forget' about the parts we have already painted... 215 216 // Draw #1 217 if (DEBUG) { 218 System.out.println("WrR: " + wrR + " srcR: " + srcR); 219 // g2d.setColor(new Color(255,0,0,128)); 220 } 221 if (ar.x < srcR.x) { 222 int w = srcR.x-ar.x; 223 if (w > ar.width) w=ar.width; 224 // g2d.fillRect(x, y, w, height); 225 dr.width = w; 226 zr.zeroRect(dr); 227 228 ar.x+=w; 229 ar.width-=w; 230 } 231 232 // Draw #2 233 if (DEBUG) { 234 System.out.println("WrR: [" + 235 ar.x + "," + ar.y + "," + 236 ar.width + "," + ar.height + 237 "] s rcR: " + srcR); 238 // g2d.setColor(new Color(0,0,255,128)); 239 } 240 if (ar.y < srcR.y) { 241 int h = srcR.y-ar.y; 242 if (h > ar.height) h=ar.height; 243 // g2d.fillRect(x, y, width, h); 244 dr.x = ar.x; 245 dr.y = ar.y; 246 dr.width = ar.width; 247 dr.height = h; 248 zr.zeroRect(dr); 249 250 ar.y +=h; 251 ar.height-=h; 252 } 253 254 // Draw #3 255 if (DEBUG) { 256 System.out.println("WrR: [" + 257 ar.x + "," + ar.y + "," + 258 ar.width + "," + ar.height + 259 "] srcR: " + srcR); 260 // g2d.setColor(new Color(0,255,0,128)); 261 } 262 if (ar.y+ar.height > srcR.y+srcR.height) { 263 int h = (ar.y+ar.height) - (srcR.y+srcR.height); 264 if (h > ar.height) h=ar.height; 265 266 int y0 = ar.y+ar.height-h; // the +/-1 cancel (?) 267 268 // g2d.fillRect(x, y0, width, h); 269 dr.x = ar.x; 270 dr.y = y0; 271 dr.width = ar.width; 272 dr.height = h; 273 zr.zeroRect(dr); 274 275 ar.height -= h; 276 } 277 278 // Draw #4 279 if (DEBUG) { 280 System.out.println("WrR: [" + 281 ar.x + "," + ar.y + "," + 282 ar.width + "," + ar.height + 283 "] srcR: " + srcR); 284 // g2d.setColor(new Color(255,255,0,128)); 285 } 286 if (ar.x+ar.width > srcR.x+srcR.width) { 287 int w = (ar.x+ar.width) - (srcR.x+srcR.width); 288 if (w > ar.width) w=ar.width; 289 int x0 = ar.x+ar.width-w; // the +/-1 cancel (?) 290 291 // g2d.fillRect(x0, y, w, height); 292 dr.x = x0; 293 dr.y = ar.y; 294 dr.width = w; 295 dr.height = ar.height; 296 zr.zeroRect(dr); 297 298 ar.width-=w; 299 } 300 } 301 302 handleReplicate(WritableRaster wr)303 protected void handleReplicate(WritableRaster wr) { 304 // Get my source. 305 CachableRed src = (CachableRed)getSources().get(0); 306 Rectangle srcR = src.getBounds(); 307 Rectangle wrR = wr.getBounds(); 308 309 int x = wrR.x; 310 int y = wrR.y; 311 int width = wrR.width; 312 int height = wrR.height; 313 314 Rectangle r; 315 { 316 // Calculate an intersection that makes some sense 317 // even when the rects don't really intersect 318 // (The x and y ranges will be correct if they 319 // overlap in one dimension even if they don't 320 // intersect in both dimensions). 321 int minX = (srcR.x > x) ? srcR.x : x; 322 int maxX = (((srcR.x+srcR.width-1) < (x+width-1)) ? 323 ( srcR.x+srcR.width-1) : (x+width-1)); 324 int minY = (srcR.y > y) ? srcR.y : y; 325 int maxY = (((srcR.y+srcR.height-1) < (y+height-1)) ? 326 ( srcR.y+srcR.height-1) : (y+height-1)); 327 328 int x0 = minX; 329 int w = maxX-minX+1; 330 int y0 = minY; 331 int h = maxY-minY+1; 332 if (w <0 ) { x0 = 0; w = 0; } 333 if (h <0 ) { y0 = 0; h = 0; } 334 r = new Rectangle(x0, y0, w, h); 335 } 336 337 // We split the edge drawing up into four parts. 338 // 339 // +-----------------------------+ 340 // | 3 | 1 | 4 | 341 // | +---------------+ | 342 // / / / / 343 // / / src / / 344 // / / / / 345 // / / / / 346 // | +---------------+ | 347 // | | 2 | | 348 // +-----------------------------+ 349 // 350 351 // Draw #1 352 if (y < srcR.y) { 353 int repW = r.width; 354 int repX = r.x; 355 int wrX = r.x; 356 int wrY = y; 357 if (x+width-1 <= srcR.x) { 358 // we are off to the left of src. so set repX to the 359 // left most pixel... 360 repW = 1; 361 repX = srcR.x; 362 wrX = x+width-1; 363 } else if (x >= srcR.x+srcR.width) { 364 // we are off to the right of src, so set repX to 365 // the right most pixel 366 repW = 1; 367 repX = srcR.x+srcR.width-1; 368 wrX = x; 369 } 370 371 // This fills the top row of section 1 from src (we 372 // go to src instead of getting the data from wr because 373 // in some cases wr will be completely off the top of src 374 WritableRaster wr1 = wr.createWritableChild(wrX, wrY, 375 repW, 1, 376 repX, srcR.y, null); 377 src.copyData(wr1); 378 wrY++; 379 380 int endY = srcR.y; 381 if (y+height < endY) endY = y+height; 382 383 if (wrY < endY) { 384 int [] pixels = wr.getPixels(wrX, wrY-1, 385 repW, 1, (int [])null); 386 while (wrY < srcR.y) { 387 wr.setPixels(wrX, wrY, repW, 1, pixels); 388 wrY++; 389 } 390 } 391 } 392 393 // Draw #2 394 if ((y+height) > (srcR.y+srcR.height)) { 395 int repW = r.width; 396 int repX = r.x; 397 int repY = srcR.y+srcR.height-1; 398 399 int wrX = r.x; 400 int wrY = srcR.y+srcR.height; 401 if (wrY < y) wrY = y; 402 403 if (x+width <= srcR.x) { 404 // we are off to the left of src. so set repX to the 405 // left most pixel... 406 repW = 1; 407 repX = srcR.x; 408 wrX = x+width-1; 409 } else if (x >= srcR.x+srcR.width) { 410 // we are off to the right of src, so set repX to 411 // the right most pixel 412 repW = 1; 413 repX = srcR.x+srcR.width-1; 414 wrX = x; 415 } 416 417 if (DEBUG) { 418 System.out.println("wr: " + wr.getBounds()); 419 System.out.println("req: [" + wrX + ", " + wrY + ", " + 420 repW + ", 1]"); 421 } 422 423 // First we get the top row of pixels from src. (we 424 // go to src instead of getting the data from wr because 425 // in some cases wr will be completely off the bottom of src). 426 WritableRaster wr1 = wr.createWritableChild(wrX, wrY, 427 repW, 1, 428 repX, repY, null); 429 // This fills the top row of section 2 from src 430 src.copyData(wr1); 431 wrY++; 432 433 int endY = y+height; 434 if (wrY < endY) { 435 // This fills the rest of section 2 from the first line. 436 int [] pixels = wr.getPixels(wrX, wrY-1, 437 repW, 1, (int [])null); 438 while (wrY < endY) { 439 wr.setPixels(wrX, wrY, repW, 1, pixels); 440 wrY++; 441 } 442 } 443 } 444 445 // Draw #3 446 if (x < srcR.x) { 447 // We are garunteed that we have a column of pixels down 448 // the edge of 1 and src. We simply replicate this column 449 // out to the edges of 2. 450 int wrX = srcR.x; 451 if (x+width <= srcR.x) { 452 wrX = x+width-1; 453 } 454 455 int xLoc = x; 456 int [] pixels = wr.getPixels(wrX, y, 1, height, (int [])null); 457 while (xLoc < wrX) { 458 wr.setPixels(xLoc, y, 1, height, pixels); 459 xLoc++; 460 } 461 } 462 463 // Draw #4 464 if (x+width > srcR.x+srcR.width) { 465 // We are garunteed that we have a column of pixels down 466 // the edge of 1 and src. We simply replicate this column 467 // out to the edges of 3. 468 int wrX = srcR.x+srcR.width-1; 469 if (x >= srcR.x+srcR.width) { 470 wrX = x; 471 } 472 473 int xLoc = wrX+1; 474 int endX = x+width-1; 475 int [] pixels = wr.getPixels(wrX, y, 1, height, (int [])null); 476 while (xLoc < endX) { 477 wr.setPixels(xLoc, y, 1, height, pixels); 478 xLoc++; 479 } 480 } 481 } 482 handleWrap(WritableRaster wr)483 protected void handleWrap(WritableRaster wr) { 484 485 handleZero(wr); 486 } 487 488 /** 489 * This function 'fixes' the source's sample model. 490 * right now it just ensures that the sample model isn't 491 * much larger than my width. 492 */ fixSampleModel(CachableRed src, Rectangle bounds)493 protected static SampleModel fixSampleModel(CachableRed src, 494 Rectangle bounds) { 495 int defSz = AbstractTiledRed.getDefaultTileSize(); 496 497 SampleModel sm = src.getSampleModel(); 498 int w = sm.getWidth(); 499 if (w < defSz) w = defSz; 500 if (w > bounds.width) w = bounds.width; 501 int h = sm.getHeight(); 502 if (h < defSz) h = defSz; 503 if (h > bounds.height) h = bounds.height; 504 505 // System.out.println("Pad SMSz: " + w + "x" + h); 506 507 return sm.createCompatibleSampleModel(w, h); 508 } 509 } 510