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