1 /*
   2  * Copyright (c) 1997, 2007, 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 
  26 package java.awt;
  27 
  28 import java.awt.image.BufferedImage;
  29 import java.awt.image.Raster;
  30 import java.awt.image.WritableRaster;
  31 import java.awt.image.ColorModel;
  32 import java.awt.image.DirectColorModel;
  33 import java.awt.image.IndexColorModel;
  34 import java.awt.geom.AffineTransform;
  35 import java.awt.geom.NoninvertibleTransformException;
  36 import java.lang.ref.WeakReference;
  37 import sun.awt.image.SunWritableRaster;
  38 import sun.awt.image.IntegerInterleavedRaster;
  39 import sun.awt.image.ByteInterleavedRaster;
  40 
  41 abstract 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     synchronized static 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     synchronized static 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     synchronized static 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     synchronized static 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 }