1/*
2 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package java.awt;
27
28import java.awt.image.BufferedImage;
29import java.awt.image.Raster;
30import java.awt.image.WritableRaster;
31import java.awt.image.ColorModel;
32import java.awt.image.DirectColorModel;
33import java.awt.image.IndexColorModel;
34import java.awt.geom.AffineTransform;
35import java.awt.geom.NoninvertibleTransformException;
36import java.lang.ref.WeakReference;
37import sun.awt.image.SunWritableRaster;
38import sun.awt.image.IntegerInterleavedRaster;
39import sun.awt.image.ByteInterleavedRaster;
40
41abstract class TexturePaintContext implements PaintContext {
42    public static ColorModel xrgbmodel =
43        new DirectColorModel(24, 0xff0000, 0xff00, 0xff);
44    public static ColorModel argbmodel = ColorModel.getRGBdefault();
45
46    ColorModel colorModel;
47    int bWidth;
48    int bHeight;
49    int maxWidth;
50
51    WritableRaster outRas;
52
53    double xOrg;
54    double yOrg;
55    double incXAcross;
56    double incYAcross;
57    double incXDown;
58    double incYDown;
59
60    int colincx;
61    int colincy;
62    int colincxerr;
63    int colincyerr;
64    int rowincx;
65    int rowincy;
66    int rowincxerr;
67    int rowincyerr;
68
69    public static PaintContext getContext(BufferedImage bufImg,
70                                          AffineTransform xform,
71                                          RenderingHints hints,
72                                          Rectangle devBounds) {
73        WritableRaster raster = bufImg.getRaster();
74        ColorModel cm = bufImg.getColorModel();
75        int maxw = devBounds.width;
76        Object val = hints.get(RenderingHints.KEY_INTERPOLATION);
77        boolean filter =
78            (val == null
79             ? (hints.get(RenderingHints.KEY_RENDERING) == RenderingHints.VALUE_RENDER_QUALITY)
80             : (val != RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR));
81        if (raster instanceof IntegerInterleavedRaster &&
82            (!filter || isFilterableDCM(cm)))
83        {
84            IntegerInterleavedRaster iir = (IntegerInterleavedRaster) raster;
85            if (iir.getNumDataElements() == 1 && iir.getPixelStride() == 1) {
86                return new Int(iir, cm, xform, maxw, filter);
87            }
88        } else if (raster instanceof ByteInterleavedRaster) {
89            ByteInterleavedRaster bir = (ByteInterleavedRaster) raster;
90            if (bir.getNumDataElements() == 1 && bir.getPixelStride() == 1) {
91                if (filter) {
92                    if (isFilterableICM(cm)) {
93                        return new ByteFilter(bir, cm, xform, maxw);
94                    }
95                } else {
96                    return new Byte(bir, cm, xform, maxw);
97                }
98            }
99        }
100        return new Any(raster, cm, xform, maxw, filter);
101    }
102
103    public static boolean isFilterableICM(ColorModel cm) {
104        if (cm instanceof IndexColorModel) {
105            IndexColorModel icm = (IndexColorModel) cm;
106            if (icm.getMapSize() <= 256) {
107                return true;
108            }
109        }
110        return false;
111    }
112
113    public static boolean isFilterableDCM(ColorModel cm) {
114        if (cm instanceof DirectColorModel) {
115            DirectColorModel dcm = (DirectColorModel) cm;
116            return (isMaskOK(dcm.getAlphaMask(), true) &&
117                    isMaskOK(dcm.getRedMask(), false) &&
118                    isMaskOK(dcm.getGreenMask(), false) &&
119                    isMaskOK(dcm.getBlueMask(), false));
120        }
121        return false;
122    }
123
124    public static boolean isMaskOK(int mask, boolean canbezero) {
125        if (canbezero && mask == 0) {
126            return true;
127        }
128        return (mask == 0xff ||
129                mask == 0xff00 ||
130                mask == 0xff0000 ||
131                mask == 0xff000000);
132    }
133
134    public static ColorModel getInternedColorModel(ColorModel cm) {
135        if (xrgbmodel == cm || xrgbmodel.equals(cm)) {
136            return xrgbmodel;
137        }
138        if (argbmodel == cm || argbmodel.equals(cm)) {
139            return argbmodel;
140        }
141        return cm;
142    }
143
144    TexturePaintContext(ColorModel cm, AffineTransform xform,
145                        int bWidth, int bHeight, int maxw) {
146        this.colorModel = getInternedColorModel(cm);
147        this.bWidth = bWidth;
148        this.bHeight = bHeight;
149        this.maxWidth = maxw;
150
151        try {
152            xform = xform.createInverse();
153        } catch (NoninvertibleTransformException e) {
154            xform.setToScale(0, 0);
155        }
156        this.incXAcross = mod(xform.getScaleX(), bWidth);
157        this.incYAcross = mod(xform.getShearY(), bHeight);
158        this.incXDown = mod(xform.getShearX(), bWidth);
159        this.incYDown = mod(xform.getScaleY(), bHeight);
160        this.xOrg = xform.getTranslateX();
161        this.yOrg = xform.getTranslateY();
162        this.colincx = (int) incXAcross;
163        this.colincy = (int) incYAcross;
164        this.colincxerr = fractAsInt(incXAcross);
165        this.colincyerr = fractAsInt(incYAcross);
166        this.rowincx = (int) incXDown;
167        this.rowincy = (int) incYDown;
168        this.rowincxerr = fractAsInt(incXDown);
169        this.rowincyerr = fractAsInt(incYDown);
170
171    }
172
173    static int fractAsInt(double d) {
174        return (int) ((d % 1.0) * Integer.MAX_VALUE);
175    }
176
177    static double mod(double num, double den) {
178        num = num % den;
179        if (num < 0) {
180            num += den;
181            if (num >= den) {
182                // For very small negative numerators, the answer might
183                // be such a tiny bit less than den that the difference
184                // is smaller than the mantissa of a double allows and
185                // the result would then be rounded to den.  If that is
186                // the case then we map that number to 0 as the nearest
187                // modulus representation.
188                num = 0;
189            }
190        }
191        return num;
192    }
193
194    /**
195     * Release the resources allocated for the operation.
196     */
197    public void dispose() {
198        dropRaster(colorModel, outRas);
199    }
200
201    /**
202     * Return the ColorModel of the output.
203     */
204    public ColorModel getColorModel() {
205        return colorModel;
206    }
207
208    /**
209     * Return a Raster containing the colors generated for the graphics
210     * operation.
211     * @param x,y,w,h The area in device space for which colors are
212     * generated.
213     */
214    public Raster getRaster(int x, int y, int w, int h) {
215        if (outRas == null ||
216            outRas.getWidth() < w ||
217            outRas.getHeight() < h)
218        {
219            // If h==1, we will probably get lots of "scanline" rects
220            outRas = makeRaster((h == 1 ? Math.max(w, maxWidth) : w), h);
221        }
222        double X = mod(xOrg + x * incXAcross + y * incXDown, bWidth);
223        double Y = mod(yOrg + x * incYAcross + y * incYDown, bHeight);
224
225        setRaster((int) X, (int) Y, fractAsInt(X), fractAsInt(Y),
226                  w, h, bWidth, bHeight,
227                  colincx, colincxerr,
228                  colincy, colincyerr,
229                  rowincx, rowincxerr,
230                  rowincy, rowincyerr);
231
232        SunWritableRaster.markDirty(outRas);
233
234        return outRas;
235    }
236
237    private static WeakReference<Raster> xrgbRasRef;
238    private static WeakReference<Raster> argbRasRef;
239
240    static synchronized WritableRaster makeRaster(ColorModel cm,
241                                                  Raster srcRas,
242                                                  int w, int h)
243    {
244        if (xrgbmodel == cm) {
245            if (xrgbRasRef != null) {
246                WritableRaster wr = (WritableRaster) xrgbRasRef.get();
247                if (wr != null && wr.getWidth() >= w && wr.getHeight() >= h) {
248                    xrgbRasRef = null;
249                    return wr;
250                }
251            }
252            // If we are going to cache this Raster, make it non-tiny
253            if (w <= 32 && h <= 32) {
254                w = h = 32;
255            }
256        } else if (argbmodel == cm) {
257            if (argbRasRef != null) {
258                WritableRaster wr = (WritableRaster) argbRasRef.get();
259                if (wr != null && wr.getWidth() >= w && wr.getHeight() >= h) {
260                    argbRasRef = null;
261                    return wr;
262                }
263            }
264            // If we are going to cache this Raster, make it non-tiny
265            if (w <= 32 && h <= 32) {
266                w = h = 32;
267            }
268        }
269        if (srcRas != null) {
270            return srcRas.createCompatibleWritableRaster(w, h);
271        } else {
272            return cm.createCompatibleWritableRaster(w, h);
273        }
274    }
275
276    static synchronized void dropRaster(ColorModel cm, Raster outRas) {
277        if (outRas == null) {
278            return;
279        }
280        if (xrgbmodel == cm) {
281            xrgbRasRef = new WeakReference<>(outRas);
282        } else if (argbmodel == cm) {
283            argbRasRef = new WeakReference<>(outRas);
284        }
285    }
286
287    private static WeakReference<Raster> byteRasRef;
288
289    static synchronized WritableRaster makeByteRaster(Raster srcRas,
290                                                      int w, int h)
291    {
292        if (byteRasRef != null) {
293            WritableRaster wr = (WritableRaster) byteRasRef.get();
294            if (wr != null && wr.getWidth() >= w && wr.getHeight() >= h) {
295                byteRasRef = null;
296                return wr;
297            }
298        }
299        // If we are going to cache this Raster, make it non-tiny
300        if (w <= 32 && h <= 32) {
301            w = h = 32;
302        }
303        return srcRas.createCompatibleWritableRaster(w, h);
304    }
305
306    static synchronized void dropByteRaster(Raster outRas) {
307        if (outRas == null) {
308            return;
309        }
310        byteRasRef = new WeakReference<>(outRas);
311    }
312
313    public abstract WritableRaster makeRaster(int w, int h);
314    public abstract void setRaster(int x, int y, int xerr, int yerr,
315                                   int w, int h, int bWidth, int bHeight,
316                                   int colincx, int colincxerr,
317                                   int colincy, int colincyerr,
318                                   int rowincx, int rowincxerr,
319                                   int rowincy, int rowincyerr);
320
321    /*
322     * Blends the four ARGB values in the rgbs array using the factors
323     * described by xmul and ymul in the following ratio:
324     *
325     *     rgbs[0] * (1-xmul) * (1-ymul) +
326     *     rgbs[1] * (  xmul) * (1-ymul) +
327     *     rgbs[2] * (1-xmul) * (  ymul) +
328     *     rgbs[3] * (  xmul) * (  ymul)
329     *
330     * xmul and ymul are integer values in the half-open range [0, 2^31)
331     * where 0 == 0.0 and 2^31 == 1.0.
332     *
333     * Note that since the range is half-open, the values are always
334     * logically less than 1.0.  This makes sense because while choosing
335     * pixels to blend, when the error values reach 1.0 we move to the
336     * next pixel and reset them to 0.0.
337     */
338    public static int blend(int rgbs[], int xmul, int ymul) {
339        // xmul/ymul are 31 bits wide, (0 => 2^31-1)
340        // shift them to 12 bits wide, (0 => 2^12-1)
341        xmul = (xmul >>> 19);
342        ymul = (ymul >>> 19);
343        int accumA, accumR, accumG, accumB;
344        accumA = accumR = accumG = accumB = 0;
345        for (int i = 0; i < 4; i++) {
346            int rgb = rgbs[i];
347            // The complement of the [xy]mul values (1-[xy]mul) can result
348            // in new values in the range (1 => 2^12).  Thus for any given
349            // loop iteration, the values could be anywhere in (0 => 2^12).
350            xmul = (1<<12) - xmul;
351            if ((i & 1) == 0) {
352                ymul = (1<<12) - ymul;
353            }
354            // xmul and ymul are each 12 bits (0 => 2^12)
355            // factor is thus 24 bits (0 => 2^24)
356            int factor = xmul * ymul;
357            if (factor != 0) {
358                // accum variables will accumulate 32 bits
359                // bytes extracted from rgb fit in 8 bits (0 => 255)
360                // byte * factor thus fits in 32 bits (0 => 255 * 2^24)
361                accumA += (((rgb >>> 24)       ) * factor);
362                accumR += (((rgb >>> 16) & 0xff) * factor);
363                accumG += (((rgb >>>  8) & 0xff) * factor);
364                accumB += (((rgb       ) & 0xff) * factor);
365            }
366        }
367        return ((((accumA + (1<<23)) >>> 24) << 24) |
368                (((accumR + (1<<23)) >>> 24) << 16) |
369                (((accumG + (1<<23)) >>> 24) <<  8) |
370                (((accumB + (1<<23)) >>> 24)      ));
371    }
372
373    static class Int extends TexturePaintContext {
374        IntegerInterleavedRaster srcRas;
375        int inData[];
376        int inOff;
377        int inSpan;
378        int outData[];
379        int outOff;
380        int outSpan;
381        boolean filter;
382
383        public Int(IntegerInterleavedRaster srcRas, ColorModel cm,
384                   AffineTransform xform, int maxw, boolean filter)
385        {
386            super(cm, xform, srcRas.getWidth(), srcRas.getHeight(), maxw);
387            this.srcRas = srcRas;
388            this.inData = srcRas.getDataStorage();
389            this.inSpan = srcRas.getScanlineStride();
390            this.inOff = srcRas.getDataOffset(0);
391            this.filter = filter;
392        }
393
394        public WritableRaster makeRaster(int w, int h) {
395            WritableRaster ras = makeRaster(colorModel, srcRas, w, h);
396            IntegerInterleavedRaster iiRas = (IntegerInterleavedRaster) ras;
397            outData = iiRas.getDataStorage();
398            outSpan = iiRas.getScanlineStride();
399            outOff = iiRas.getDataOffset(0);
400            return ras;
401        }
402
403        public void setRaster(int x, int y, int xerr, int yerr,
404                              int w, int h, int bWidth, int bHeight,
405                              int colincx, int colincxerr,
406                              int colincy, int colincyerr,
407                              int rowincx, int rowincxerr,
408                              int rowincy, int rowincyerr) {
409            int[] inData = this.inData;
410            int[] outData = this.outData;
411            int out = outOff;
412            int inSpan = this.inSpan;
413            int inOff = this.inOff;
414            int outSpan = this.outSpan;
415            boolean filter = this.filter;
416            boolean normalx = (colincx == 1 && colincxerr == 0 &&
417                               colincy == 0 && colincyerr == 0) && !filter;
418            int rowx = x;
419            int rowy = y;
420            int rowxerr = xerr;
421            int rowyerr = yerr;
422            if (normalx) {
423                outSpan -= w;
424            }
425            int rgbs[] = filter ? new int[4] : null;
426            for (int j = 0; j < h; j++) {
427                if (normalx) {
428                    int in = inOff + rowy * inSpan + bWidth;
429                    x = bWidth - rowx;
430                    out += w;
431                    if (bWidth >= 32) {
432                        int i = w;
433                        while (i > 0) {
434                            int copyw = (i < x) ? i : x;
435                            System.arraycopy(inData, in - x,
436                                             outData, out - i,
437                                             copyw);
438                            i -= copyw;
439                            if ((x -= copyw) == 0) {
440                                x = bWidth;
441                            }
442                        }
443                    } else {
444                        for (int i = w; i > 0; i--) {
445                            outData[out - i] = inData[in - x];
446                            if (--x == 0) {
447                                x = bWidth;
448                            }
449                        }
450                    }
451                } else {
452                    x = rowx;
453                    y = rowy;
454                    xerr = rowxerr;
455                    yerr = rowyerr;
456                    for (int i = 0; i < w; i++) {
457                        if (filter) {
458                            int nextx, nexty;
459                            if ((nextx = x + 1) >= bWidth) {
460                                nextx = 0;
461                            }
462                            if ((nexty = y + 1) >= bHeight) {
463                                nexty = 0;
464                            }
465                            rgbs[0] = inData[inOff + y * inSpan + x];
466                            rgbs[1] = inData[inOff + y * inSpan + nextx];
467                            rgbs[2] = inData[inOff + nexty * inSpan + x];
468                            rgbs[3] = inData[inOff + nexty * inSpan + nextx];
469                            outData[out + i] =
470                                TexturePaintContext.blend(rgbs, xerr, yerr);
471                        } else {
472                            outData[out + i] = inData[inOff + y * inSpan + x];
473                        }
474                        if ((xerr += colincxerr) < 0) {
475                            xerr &= Integer.MAX_VALUE;
476                            x++;
477                        }
478                        if ((x += colincx) >= bWidth) {
479                            x -= bWidth;
480                        }
481                        if ((yerr += colincyerr) < 0) {
482                            yerr &= Integer.MAX_VALUE;
483                            y++;
484                        }
485                        if ((y += colincy) >= bHeight) {
486                            y -= bHeight;
487                        }
488                    }
489                }
490                if ((rowxerr += rowincxerr) < 0) {
491                    rowxerr &= Integer.MAX_VALUE;
492                    rowx++;
493                }
494                if ((rowx += rowincx) >= bWidth) {
495                    rowx -= bWidth;
496                }
497                if ((rowyerr += rowincyerr) < 0) {
498                    rowyerr &= Integer.MAX_VALUE;
499                    rowy++;
500                }
501                if ((rowy += rowincy) >= bHeight) {
502                    rowy -= bHeight;
503                }
504                out += outSpan;
505            }
506        }
507    }
508
509    static class Byte extends TexturePaintContext {
510        ByteInterleavedRaster srcRas;
511        byte inData[];
512        int inOff;
513        int inSpan;
514        byte outData[];
515        int outOff;
516        int outSpan;
517
518        public Byte(ByteInterleavedRaster srcRas, ColorModel cm,
519                    AffineTransform xform, int maxw)
520        {
521            super(cm, xform, srcRas.getWidth(), srcRas.getHeight(), maxw);
522            this.srcRas = srcRas;
523            this.inData = srcRas.getDataStorage();
524            this.inSpan = srcRas.getScanlineStride();
525            this.inOff = srcRas.getDataOffset(0);
526        }
527
528        public WritableRaster makeRaster(int w, int h) {
529            WritableRaster ras = makeByteRaster(srcRas, w, h);
530            ByteInterleavedRaster biRas = (ByteInterleavedRaster) ras;
531            outData = biRas.getDataStorage();
532            outSpan = biRas.getScanlineStride();
533            outOff = biRas.getDataOffset(0);
534            return ras;
535        }
536
537        public void dispose() {
538            dropByteRaster(outRas);
539        }
540
541        public void setRaster(int x, int y, int xerr, int yerr,
542                              int w, int h, int bWidth, int bHeight,
543                              int colincx, int colincxerr,
544                              int colincy, int colincyerr,
545                              int rowincx, int rowincxerr,
546                              int rowincy, int rowincyerr) {
547            byte[] inData = this.inData;
548            byte[] outData = this.outData;
549            int out = outOff;
550            int inSpan = this.inSpan;
551            int inOff = this.inOff;
552            int outSpan = this.outSpan;
553            boolean normalx = (colincx == 1 && colincxerr == 0 &&
554                               colincy == 0 && colincyerr == 0);
555            int rowx = x;
556            int rowy = y;
557            int rowxerr = xerr;
558            int rowyerr = yerr;
559            if (normalx) {
560                outSpan -= w;
561            }
562            for (int j = 0; j < h; j++) {
563                if (normalx) {
564                    int in = inOff + rowy * inSpan + bWidth;
565                    x = bWidth - rowx;
566                    out += w;
567                    if (bWidth >= 32) {
568                        int i = w;
569                        while (i > 0) {
570                            int copyw = (i < x) ? i : x;
571                            System.arraycopy(inData, in - x,
572                                             outData, out - i,
573                                             copyw);
574                            i -= copyw;
575                            if ((x -= copyw) == 0) {
576                                x = bWidth;
577                            }
578                        }
579                    } else {
580                        for (int i = w; i > 0; i--) {
581                            outData[out - i] = inData[in - x];
582                            if (--x == 0) {
583                                x = bWidth;
584                            }
585                        }
586                    }
587                } else {
588                    x = rowx;
589                    y = rowy;
590                    xerr = rowxerr;
591                    yerr = rowyerr;
592                    for (int i = 0; i < w; i++) {
593                        outData[out + i] = inData[inOff + y * inSpan + x];
594                        if ((xerr += colincxerr) < 0) {
595                            xerr &= Integer.MAX_VALUE;
596                            x++;
597                        }
598                        if ((x += colincx) >= bWidth) {
599                            x -= bWidth;
600                        }
601                        if ((yerr += colincyerr) < 0) {
602                            yerr &= Integer.MAX_VALUE;
603                            y++;
604                        }
605                        if ((y += colincy) >= bHeight) {
606                            y -= bHeight;
607                        }
608                    }
609                }
610                if ((rowxerr += rowincxerr) < 0) {
611                    rowxerr &= Integer.MAX_VALUE;
612                    rowx++;
613                }
614                if ((rowx += rowincx) >= bWidth) {
615                    rowx -= bWidth;
616                }
617                if ((rowyerr += rowincyerr) < 0) {
618                    rowyerr &= Integer.MAX_VALUE;
619                    rowy++;
620                }
621                if ((rowy += rowincy) >= bHeight) {
622                    rowy -= bHeight;
623                }
624                out += outSpan;
625            }
626        }
627    }
628
629    static class ByteFilter extends TexturePaintContext {
630        ByteInterleavedRaster srcRas;
631        int inPalette[];
632        byte inData[];
633        int inOff;
634        int inSpan;
635        int outData[];
636        int outOff;
637        int outSpan;
638
639        public ByteFilter(ByteInterleavedRaster srcRas, ColorModel cm,
640                          AffineTransform xform, int maxw)
641        {
642            super((cm.getTransparency() == Transparency.OPAQUE
643                   ? xrgbmodel : argbmodel),
644                  xform, srcRas.getWidth(), srcRas.getHeight(), maxw);
645            this.inPalette = new int[256];
646            ((IndexColorModel) cm).getRGBs(this.inPalette);
647            this.srcRas = srcRas;
648            this.inData = srcRas.getDataStorage();
649            this.inSpan = srcRas.getScanlineStride();
650            this.inOff = srcRas.getDataOffset(0);
651        }
652
653        public WritableRaster makeRaster(int w, int h) {
654            // Note that we do not pass srcRas to makeRaster since it
655            // is a Byte Raster and this colorModel needs an Int Raster
656            WritableRaster ras = makeRaster(colorModel, null, w, h);
657            IntegerInterleavedRaster iiRas = (IntegerInterleavedRaster) ras;
658            outData = iiRas.getDataStorage();
659            outSpan = iiRas.getScanlineStride();
660            outOff = iiRas.getDataOffset(0);
661            return ras;
662        }
663
664        public void setRaster(int x, int y, int xerr, int yerr,
665                              int w, int h, int bWidth, int bHeight,
666                              int colincx, int colincxerr,
667                              int colincy, int colincyerr,
668                              int rowincx, int rowincxerr,
669                              int rowincy, int rowincyerr) {
670            byte[] inData = this.inData;
671            int[] outData = this.outData;
672            int out = outOff;
673            int inSpan = this.inSpan;
674            int inOff = this.inOff;
675            int outSpan = this.outSpan;
676            int rowx = x;
677            int rowy = y;
678            int rowxerr = xerr;
679            int rowyerr = yerr;
680            int rgbs[] = new int[4];
681            for (int j = 0; j < h; j++) {
682                x = rowx;
683                y = rowy;
684                xerr = rowxerr;
685                yerr = rowyerr;
686                for (int i = 0; i < w; i++) {
687                    int nextx, nexty;
688                    if ((nextx = x + 1) >= bWidth) {
689                        nextx = 0;
690                    }
691                    if ((nexty = y + 1) >= bHeight) {
692                        nexty = 0;
693                    }
694                    rgbs[0] = inPalette[0xff & inData[inOff + x +
695                                                      inSpan * y]];
696                    rgbs[1] = inPalette[0xff & inData[inOff + nextx +
697                                                      inSpan * y]];
698                    rgbs[2] = inPalette[0xff & inData[inOff + x +
699                                                      inSpan * nexty]];
700                    rgbs[3] = inPalette[0xff & inData[inOff + nextx +
701                                                      inSpan * nexty]];
702                    outData[out + i] =
703                        TexturePaintContext.blend(rgbs, xerr, yerr);
704                    if ((xerr += colincxerr) < 0) {
705                        xerr &= Integer.MAX_VALUE;
706                        x++;
707                    }
708                    if ((x += colincx) >= bWidth) {
709                        x -= bWidth;
710                    }
711                    if ((yerr += colincyerr) < 0) {
712                        yerr &= Integer.MAX_VALUE;
713                        y++;
714                    }
715                    if ((y += colincy) >= bHeight) {
716                        y -= bHeight;
717                    }
718                }
719                if ((rowxerr += rowincxerr) < 0) {
720                    rowxerr &= Integer.MAX_VALUE;
721                    rowx++;
722                }
723                if ((rowx += rowincx) >= bWidth) {
724                    rowx -= bWidth;
725                }
726                if ((rowyerr += rowincyerr) < 0) {
727                    rowyerr &= Integer.MAX_VALUE;
728                    rowy++;
729                }
730                if ((rowy += rowincy) >= bHeight) {
731                    rowy -= bHeight;
732                }
733                out += outSpan;
734            }
735        }
736    }
737
738    static class Any extends TexturePaintContext {
739        WritableRaster srcRas;
740        boolean filter;
741
742        public Any(WritableRaster srcRas, ColorModel cm,
743                   AffineTransform xform, int maxw, boolean filter)
744        {
745            super(cm, xform, srcRas.getWidth(), srcRas.getHeight(), maxw);
746            this.srcRas = srcRas;
747            this.filter = filter;
748        }
749
750        public WritableRaster makeRaster(int w, int h) {
751            return makeRaster(colorModel, srcRas, w, h);
752        }
753
754        public void setRaster(int x, int y, int xerr, int yerr,
755                              int w, int h, int bWidth, int bHeight,
756                              int colincx, int colincxerr,
757                              int colincy, int colincyerr,
758                              int rowincx, int rowincxerr,
759                              int rowincy, int rowincyerr) {
760            Object data = null;
761            int rowx = x;
762            int rowy = y;
763            int rowxerr = xerr;
764            int rowyerr = yerr;
765            WritableRaster srcRas = this.srcRas;
766            WritableRaster outRas = this.outRas;
767            int rgbs[] = filter ? new int[4] : null;
768            for (int j = 0; j < h; j++) {
769                x = rowx;
770                y = rowy;
771                xerr = rowxerr;
772                yerr = rowyerr;
773                for (int i = 0; i < w; i++) {
774                    data = srcRas.getDataElements(x, y, data);
775                    if (filter) {
776                        int nextx, nexty;
777                        if ((nextx = x + 1) >= bWidth) {
778                            nextx = 0;
779                        }
780                        if ((nexty = y + 1) >= bHeight) {
781                            nexty = 0;
782                        }
783                        rgbs[0] = colorModel.getRGB(data);
784                        data = srcRas.getDataElements(nextx, y, data);
785                        rgbs[1] = colorModel.getRGB(data);
786                        data = srcRas.getDataElements(x, nexty, data);
787                        rgbs[2] = colorModel.getRGB(data);
788                        data = srcRas.getDataElements(nextx, nexty, data);
789                        rgbs[3] = colorModel.getRGB(data);
790                        int rgb =
791                            TexturePaintContext.blend(rgbs, xerr, yerr);
792                        data = colorModel.getDataElements(rgb, data);
793                    }
794                    outRas.setDataElements(i, j, data);
795                    if ((xerr += colincxerr) < 0) {
796                        xerr &= Integer.MAX_VALUE;
797                        x++;
798                    }
799                    if ((x += colincx) >= bWidth) {
800                        x -= bWidth;
801                    }
802                    if ((yerr += colincyerr) < 0) {
803                        yerr &= Integer.MAX_VALUE;
804                        y++;
805                    }
806                    if ((y += colincy) >= bHeight) {
807                        y -= bHeight;
808                    }
809                }
810                if ((rowxerr += rowincxerr) < 0) {
811                    rowxerr &= Integer.MAX_VALUE;
812                    rowx++;
813                }
814                if ((rowx += rowincx) >= bWidth) {
815                    rowx -= bWidth;
816                }
817                if ((rowyerr += rowincyerr) < 0) {
818                    rowyerr &= Integer.MAX_VALUE;
819                    rowy++;
820                }
821                if ((rowy += rowincy) >= bHeight) {
822                    rowy -= bHeight;
823                }
824            }
825        }
826    }
827}
828