Downloads containing Resize v10.asc

Downloads
Name Author Game Mode Rating
TSF with JJ2+ Only: Nyan Rush sAlAmAnDeR Mutator N/A Download file
JJ2+ Only: Foo Single Player 2/14:...Featured Download Violet CLM Single player 10 Download file
TSF with JJ2+ Only: Operation Cleanup: Turtle...Featured Download happygreenfrog Single player 8.2 Download file

File preview

  1. //Resize v10.asc: version 1.0 (2018/02/08)
  2. #pragma require "Resize v10.asc"
  3. #include "TrueColor v12.asc"
  4.  
  5. /*
  6. Resize v10.asc lets scriptwriters resize any image, animframe, animation, or animset by any positive multipliers, choosing from a number of different algorithms to see what looks best on any given image (sequence), unlike JJ2+'s normal jjDrawResizedSprite function which only uses nearest neighbor resizing.
  7.  
  8. SAMPLE SCRIPT:
  9.         void onLevelLoad() {
  10.                 Resize::Resize(jjAnimSets[ANIM::RAVEN], 3, 3.0, Resize::Method::Scale2xSAI);
  11.         }
  12.  
  13.        
  14. API:
  15.         jjPIXELMAP@ Resize::Resize(const jjPIXELMAP &in input, float scaleX, uint method = Resize::Method::NearestNeighbor, float scaleY = 0.f)
  16.                 Takes an existing jjPIXELMAP object and returns a new one. The returned jjPIXELMAP's width will be equal to scaleX * input.width and its height scaleY * input.height, and the image will be resized accordingly. If scaleY is left at its default value, it will be set to the same value as scaleX instead, meaning the aspect ratio will stay constant. The effects of negative scale values are undefined.
  17.                
  18.                 For the method argument, you are encouraged to pass a member of the Resize::Method enum. There are two categories of these:
  19.                         Algorithms which do not introduce new colors: NearestNeighbor, Scale2x, AdvMAME2x, Eagle
  20.                         Algorithms which blend adjacent colors and thereby introduce new colors: BilinearInterpolation, Regular2xSAI, Scale2xSAI
  21.                 For more information about how these different algorithms differ you are referred to the internet.
  22.                
  23.                 There are also two values in the Resize::Flags enum you may find useful for the method argument. These should be bitwise-OR'd together, e.g. "Resize::Method::Scale2x | Resize::Flags::EdgesRepeat".
  24.                         EdgesRepeat: Toggles whether the resizing algorithms should consider the image to repeat indefinitely outside the boundaries of the pixelmap, or to be surrounded by transparent pixels.
  25.                         OnlySpriteColors: Has an effect only for the algorithms that introduce new colors, restricting them to colors found in the standard sprite palette. You should use this flag if you anticipate calling jjPAL::apply after resizing any sprites.
  26.                        
  27.                        
  28.         TrueColor::Bitmap@ Resize(const TrueColor::Bitmap &in input, float scaleX, uint method = Resize::Method::NearestNeighbor, float scaleY = 0.f)
  29.                 Same as the above function, but instead of jjPIXELMAP objects, it works on Bitmap objects from the TrueColor library. The Resize::Flags::OnlySpriteColors flag is ignored when working with TrueColor images, because it would be meaningless.
  30.                        
  31.                        
  32.         void Resize::Resize(jjANIMFRAME & frame, float scaleX, uint method = Resize::Method::NearestNeighbor, float scaleY = 0.f)
  33.                 Resizes the image used by a jjANIMFRAME, and also multiplies that frame's hotspot, gunspot, and coldspot properties by the scale arguments. Note that unlike the above two functions, which return new objects, this one modifies the jjANIMFRAME in place.
  34.        
  35.        
  36.         void Resize::Resize(const jjANIMATION & anim, float scaleX, uint method = Resize::Method::NearestNeighbor, float scaleY = 0.f)
  37.         void Resize::Resize(jjANIMSET & set, uint animCount, float scaleX, uint method = Resize::Method::NearestNeighbor, float scaleY = 0.f)
  38.                 Convenience functions for resizing multiple jjANIMFRAMEs at once while keeping the method and scale values constant. Note that the latter function requires you to state explicitly how many animations are used by the jjANIMSET (e.g. 2 for ANIM::FISH), because JJ2 does not store that number anywhere.
  39.                
  40.                
  41.         void Resize::AllocateAndResizeTrueColorSpriteSheet(const ANIM::Set setID, const TrueColor::Bitmap& bitmap, const array<array<TrueColor::Coordinates>>& setCoordinates, float scaleX, uint method = Resize::Method::NearestNeighbor, float scaleY = 0.f)
  42.                 Works the same (and has roughly the same signature) as TrueColor::AllocateSpriteSheet, but resizes each image (in accordance with scaleX/scaleY/method) from the sprite sheet before saving it. The coordinates in the setCoordinates array should refer to the coordinates within the image BEFORE they are resized.
  43.                        
  44. WARNING: All internal code details are subject to change, and any properties, methods, or functions NOT described above should not be used by external scripts for risk of failing to work in later updates to this library.
  45.  
  46. */
  47.  
  48.  
  49.  
  50.  
  51.  
  52.  
  53.  
  54.  
  55.  
  56.  
  57.  
  58.  
  59.  
  60.  
  61.  
  62.  
  63.  
  64.  
  65.  
  66.  
  67.  
  68.  
  69.  
  70.  
  71.  
  72.  
  73.  
  74.  
  75.  
  76.  
  77. namespace Resize {
  78.         enum Method {
  79.                 NearestNeighbor, Scale2x, AdvMAME2x, Eagle, _firstInvolvingFindNearestColor, BilinearInterpolation, Regular2xSAI, Scale2xSAI, Super2xSAI, SuperEagle, HQ2x, xBR, //some of these don't work
  80.                 //http://supercomputingblog.com/graphics/coding-bilinear-interpolation/
  81.                 //http://www.scale2x.it/algorithm
  82.                 //https://github.com/Treeki/libxbr-standalone
  83.                 //https://vdnoort.home.xs4all.nl/emulation/2xsai
  84.                 //https://en.wikipedia.org/wiki/Pixel-art_scaling_algorithms
  85.         };
  86.         enum Flags {
  87.                 None = 0, _firstFlag = 1<<5, OnlySpriteColors = _firstFlag, EdgesRepeat = 1<<6
  88.         };
  89.         bool _edgesRepeat;
  90.        
  91.         class _Color {
  92.                 uint8 index = 0;
  93.                 jjPALCOLOR color;
  94.                 bool isPaletted = true;
  95.                 _Color(uint8 i) { index = i; color = jjPalette.color[i]; }
  96.                 _Color(const jjPALCOLOR &in c) { color = c; isPaletted = false; }
  97.                 bool opEquals(const _Color &in other) const {
  98.                         if (!isPaletted) return color == other.color;
  99.                         return index == other.index;
  100.                 }
  101.         }
  102.        
  103.         class _Image {
  104.                 jjPIXELMAP@ pixelmap = null;
  105.                 TrueColor::Bitmap@ bitmap = null;
  106.                 uint width, height;
  107.                 _Image(const jjPIXELMAP &in pm) {
  108.                         @pixelmap = jjPIXELMAP(width = pm.width, height = pm.height);
  109.                         for (int x = width - 1; x >= 0; --x)
  110.                                 for (int y = height - 1; y >= 0; --y)
  111.                                         pixelmap[x,y] = pm[x,y]; //no native copy constructor available
  112.                 }
  113.                 _Image(const TrueColor::Bitmap &in bm) {
  114.                         @bitmap = TrueColor::Bitmap(bm, 0, 0, width = bm.width, height = bm.height); //copy constructor
  115.                 }
  116.                 _Image(const _Image &in i, uint w, uint h) {
  117.                         width = w; height = h;
  118.                         if (i.bitmap is null)
  119.                                 @pixelmap = jjPIXELMAP(w, h);
  120.                         else
  121.                                 @bitmap = TrueColor::Bitmap(w, h);
  122.                 }
  123.                
  124.                 _Color@ Get(int x, int y) const {
  125.                         if (!_edgesRepeat) {
  126.                                 if (x < 0 ||
  127.                                 y < 0 ||
  128.                                 uint(x) >= width ||
  129.                                 uint(y) >= height)
  130.                                         return (bitmap is null) ? _Color(0) : _Color(jjPALCOLOR());
  131.                         } else {
  132.                                 if (x < 0) x = 0;
  133.                                 else if (uint(x) >= width) x = width - 1;
  134.                                 if (y < 0) y = 0;
  135.                                 else if (uint(y) >= height) y = height - 1;
  136.                         }
  137.                        
  138.                         return (bitmap is null) ? _Color(pixelmap[x,y]) : _Color(bitmap.pixels[x][y]);
  139.                 }
  140.                 void Set(int x, int y, const _Color &in color) {
  141.                         if (x < 0 ||
  142.                         y < 0 ||
  143.                         uint(x) >= width ||
  144.                         uint(y) >= height)
  145.                                 return;
  146.                        
  147.                         if (bitmap is null) {
  148.                                 if (color.isPaletted)
  149.                                         pixelmap[x,y] = color.index;
  150.                                 else {
  151.                                         const uint8 index = _palette.findNearestColor(color.color);
  152.                                         const jjPALCOLOR foundColor = _palette.color[index];
  153.                                         if (foundColor.red == 0 && foundColor.green == 0 && foundColor.blue == 0) pixelmap[x,y] = 0; //black = transparent
  154.                                         else pixelmap[x,y] = index;
  155.                                 }
  156.                         } else
  157.                                 bitmap.pixels[x][y] = color.color;
  158.                 }
  159.                 void Set(int x, int y, const jjPALCOLOR &in color) {
  160.                         this.Set(x, y, _Color(color));
  161.                 }
  162.         }
  163.        
  164.         jjPAL _palette;
  165.        
  166.         _Image@ Resize(_Image@ input, float scaleX, uint method = Resize::Method::NearestNeighbor, float scaleY = 0.f) {
  167.                 scaleX = abs(scaleX); //undefined?
  168.                 if (scaleY == 0.f) scaleY = scaleX;
  169.                 else scaleY = abs(scaleY);
  170.                                
  171.                 if (scaleX == 1.f && scaleY == 1.f)
  172.                         return @input;
  173.                
  174.                 const auto width = uint(input.width * scaleX), height = uint(input.height * scaleY);
  175.                
  176.                 _palette = jjPalette;
  177.                 const uint flags = method & ~(Flags::_firstFlag - 1);
  178.                 method ^= flags;
  179.                 if ((flags & Flags::OnlySpriteColors) != 0 && input.bitmap is null && method >= _firstInvolvingFindNearestColor) {
  180.                         jjPALCOLOR black;
  181.                         _palette.fill(black, 0, 15); //start
  182.                         _palette.fill(black, 56, 3); //yellow gap
  183.                         _palette.fill(black, 96, 159); //remainder + finish
  184.                 }
  185.                 _edgesRepeat = (flags & Flags::EdgesRepeat) != 0;
  186.                        
  187.                 //non-doubling-based algorithms which work at any size
  188.                 if (method == Method::BilinearInterpolation)
  189.                         return _bilinearInterpolation(input, width,height);
  190.                 if (method == Method::Scale2xSAI)
  191.                         return _scale_2xSaI(input, width,height);
  192.                
  193.                 if ((scaleX > 1.f || scaleY > 1.f) && method != Method::NearestNeighbor) {
  194.                         float progressiveScale = scaleX;
  195.                         if (scaleY > progressiveScale) progressiveScale = scaleY; //max of two
  196.                         while (progressiveScale > 1.f) {
  197.                                 @input = @_double(input, method);
  198.                                 progressiveScale /= 2;
  199.                         }
  200.                         if (progressiveScale == 1.f && scaleX == scaleY)
  201.                                 return @input;
  202.                 }
  203.                
  204.                 return _nearestNeighbor(input, width,height);  //default, or simply last step in case of downscaling (after upscaling or otherwise)
  205.         }
  206.         jjPIXELMAP@ Resize(const jjPIXELMAP &in input, float scaleX, uint method = Resize::Method::NearestNeighbor, float scaleY = 0.f) {
  207.                 return Resize(_Image(input), scaleX, method, scaleY).pixelmap;
  208.         }
  209.         TrueColor::Bitmap@ Resize(const TrueColor::Bitmap &in input, float scaleX, uint method = Resize::Method::NearestNeighbor, float scaleY = 0.f) {
  210.                 return Resize(_Image(input), scaleX, method, scaleY).bitmap;
  211.         }
  212.        
  213.         void Resize(jjANIMFRAME & frame, float scaleX, uint method = Resize::Method::NearestNeighbor, float scaleY = 0.f) {
  214.                 if (scaleY == 0.f) scaleY = scaleX;
  215.                 else scaleY = abs(scaleY);
  216.                 const uint oldWidth = frame.width;
  217.                 const uint oldHeight = frame.height;
  218.                 Resize(jjPIXELMAP(frame), scaleX, method, scaleY).save(frame);
  219.                 frame.hotSpotX = int(frame.hotSpotX * scaleX);
  220.                 frame.hotSpotY = int(frame.hotSpotY * scaleY);
  221.                 frame.gunSpotX = int(frame.gunSpotX * scaleX);
  222.                 frame.gunSpotY = int(frame.gunSpotY * scaleY);
  223.                 frame.coldSpotX = int(frame.coldSpotX * scaleX); //not that this accomplishes anything
  224.                 frame.coldSpotY = int(frame.coldSpotY * scaleY);
  225.         }
  226.        
  227.         void Resize(const jjANIMATION & anim, float scaleX, uint method = Resize::Method::NearestNeighbor, float scaleY = 0.f) {
  228.                 const uint numFrames = anim.frameCount;
  229.                 for (uint i = 0; i < numFrames; ++i)
  230.                         Resize(jjAnimFrames[anim + i], scaleX, method, scaleY);
  231.         }
  232.         void Resize(jjANIMSET & set, uint animCount, float scaleX, uint method = Resize::Method::NearestNeighbor, float scaleY = 0.f) {
  233.                 if (set == 0)
  234.                         set.load();
  235.                 for (uint i = 0; i < animCount; ++i)
  236.                         Resize(jjAnimations[set + i], scaleX, method, scaleY);
  237.         }
  238.        
  239.         _Image@ _double(const _Image &in input, uint method) {
  240.                 switch (method) {
  241.                         case Method::Scale2x:
  242.                                 return _scale2x(input);
  243.                         case Method::AdvMAME2x:
  244.                                 return _advMAME2x(input);
  245.                         case Method::Eagle:
  246.                                 return _eagle(input);
  247.                         case Method::Regular2xSAI:
  248.                                 return _2xSAI(input);
  249.                         /*case Method::xBR:
  250.                                 return _xBR(input);
  251.                         case Method::HQ2x:
  252.                                 return _HQ2x(input);
  253.                         case Method::Super2xSAI:
  254.                                 return _super2xSAI(input);
  255.                         case Method::SuperEagle:
  256.                                 return _superEagle(input);*/
  257.                 }
  258.                 //default: not yet implemented, etc.
  259.                 return _nearestNeighbor(input, input.width*2, input.height*2);
  260.         }
  261.        
  262.        
  263.        
  264.         void AllocateAndResizeTrueColorSpriteSheet(const ANIM::Set setID, const TrueColor::Bitmap& bitmap, const array<array<TrueColor::Coordinates>>& setCoordinates, float scaleX, uint method = Resize::Method::NearestNeighbor, float scaleY = 0.f) {
  265.                 if (scaleX == 1.f && scaleY == 1.f) {
  266.                         TrueColor::AllocateSpriteSheet(setID, bitmap, setCoordinates);
  267.                         return;
  268.                 }
  269.                 scaleX = abs(scaleX); //undefined?
  270.                 if (scaleY == 0.f) scaleY = scaleX;
  271.                 else scaleY = abs(scaleY);
  272.                
  273.                 array<uint> animSizes(setCoordinates.length);
  274.                 for (uint i = 0; i < animSizes.length; ++i) {
  275.                         animSizes[i] = setCoordinates[i].length * TrueColor::NumberOfFramesPerImage;
  276.                 }
  277.                 jjAnimSets[setID].allocate(animSizes);
  278.                 for (uint animID = 0; animID < animSizes.length; ++animID) {
  279.                         const array<TrueColor::Coordinates>@ animCoordinates = @setCoordinates[animID];
  280.                         for (uint frameID = 0; frameID < animCoordinates.length; ++frameID) {
  281.                                 const TrueColor::Coordinates@ coordinates = animCoordinates[frameID];
  282.                                 const TrueColor::Bitmap@ sprite = TrueColor::Bitmap(bitmap, coordinates.left, coordinates.top, coordinates.width, coordinates.height);
  283.                                 @sprite = Resize::Resize(sprite, scaleX, method, scaleY);
  284.                                 sprite.saveToAnimFrames(
  285.                                         jjAnimations[jjAnimSets[setID].firstAnim + animID].firstFrame + frameID * TrueColor::NumberOfFramesPerImage,
  286.                                         TrueColor::Coordinates(
  287.                                                 0, 0,
  288.                                                 sprite.width, sprite.height,
  289.                                                 int(coordinates.hotSpotX * scaleX), int(coordinates.hotSpotY * scaleY),
  290.                                                 int(coordinates.gunSpotX * scaleX), int(coordinates.gunSpotY * scaleY),
  291.                                                 int(coordinates.coldSpotX * scaleX), int(coordinates.coldSpotY * scaleY),
  292.                                                 coordinates.transparent,
  293.                                                 coordinates.swingingVine
  294.                                         )
  295.                                 );
  296.                         }
  297.                 }
  298.         }
  299.        
  300.        
  301.        
  302.         _Image@ _nearestNeighbor(const _Image &in input, uint width, uint height) {
  303.                 _Image output(input, width, height);
  304.                 for (int x = width - 1; x >= 0; --x)
  305.                         for (int y = height - 1; y >= 0; --y)
  306.                                 output.Set(x, y, input.Get(x * input.width / width, y * input.height / height));
  307.                 return output;
  308.         }
  309.        
  310.         _Image@ _bilinearInterpolation(const _Image &in input, uint width, uint height) {
  311.                 _Image output(input, width, height);
  312.                 for (int x = width - 1; x >= 0; --x) {
  313.                         const float
  314.                                 xx = float(x) * input.width / width,
  315.                                 x1 = floor(xx),
  316.                                 x2 = x1+1,
  317.                                 RX = (x2 - xx);
  318.                         for (int y = height - 1; y >= 0; --y) {
  319.                                 const float
  320.                                         yy = float(y) * input.height / height,
  321.                                         y1 = floor(yy),
  322.                                         y2 = y1+1,
  323.                                         RY = (y2 - yy);
  324.                                 const jjPALCOLOR
  325.                                         TopLeft = input.Get(int(x1), int(y1)).color,
  326.                                         TopRight = input.Get(int(x2), int(y1)).color,
  327.                                         BottomLeft = input.Get(int(x1), int(y2)).color,
  328.                                         BottomRight = input.Get(int(x2), int(y2)).color;
  329.                                        
  330.                                 output.Set(x,y, _blendColors(_blendColors(TopLeft,TopRight,RX), _blendColors(BottomLeft,BottomRight,RX), RY));
  331.                         }
  332.                 }
  333.                 return output;
  334.         }
  335.        
  336.         _Image@ _scale2x(const _Image &in input) {
  337.                 _Image output(input, input.width * 2, input.height * 2);
  338.                 for (int x = output.width - 2, xx = x>>1; x >= 0; x -= 2, --xx)
  339.                         for (int y = output.height - 2, yy = y>>1; y >= 0; y -= 2, --yy) {
  340.                                 const _Color@
  341.                                         B = input.Get(xx, yy-1),
  342.                                         D = input.Get(xx-1, yy),
  343.                                         E = input.Get(xx,yy),
  344.                                         F = input.Get(xx+1, yy),
  345.                                         H = input.Get(xx, yy+1);
  346.                                 if (B != H && D != F) {
  347.                                         const _Color@ result; //needs to be a separate variable to fix an angelscript bug
  348.                                         @result = (D == B) ? D : E; output.Set(x,y, result);
  349.                                         @result = (B == F) ? F : E; output.Set(x+1,y, result);
  350.                                         @result = (D == H) ? D : E; output.Set(x,y+1, result);
  351.                                         @result = (H == F) ? F : E; output.Set(x+1,y+1, result);
  352.                                 } else {
  353.                                         output.Set(x,y, E);
  354.                                         output.Set(x+1,y, E);
  355.                                         output.Set(x,y+1, E);
  356.                                         output.Set(x+1,y+1, E);
  357.                                 }
  358.                         }
  359.                 return output;
  360.         }
  361.        
  362.         _Image@ _advMAME2x(const _Image &in input) {
  363.                 _Image output(input, input.width * 2, input.height * 2);
  364.                 for (int x = output.width - 2, xx = x>>1; x >= 0; x -= 2, --xx)
  365.                         for (int y = output.height - 2, yy = y>>1; y >= 0; y -= 2, --yy) {
  366.                                 const _Color@
  367.                                         A = input.Get(xx, yy-1),
  368.                                         C = input.Get(xx-1, yy),
  369.                                         P = input.Get(xx,yy),
  370.                                         B = input.Get(xx+1, yy),
  371.                                         D = input.Get(xx, yy+1);
  372.                                 const _Color@ result; //needs to be a separate variable to fix an angelscript bug
  373.                                 @result = (C==A && C!=D && A!=B) ? A : P; output.Set(x,y, result);
  374.                                 @result = (A==B && A!=C && B!=D) ? B : P; output.Set(x+1,y, result);
  375.                                 @result = (D==C && D!=B && C!=A) ? C : P; output.Set(x,y+1, result);
  376.                                 @result = (B==D && B!=A && D!=C) ? D : P; output.Set(x+1,y+1, result);
  377.                         }
  378.                 return output;
  379.         }
  380.        
  381.         _Image@ _eagle(const _Image &in input) {
  382.                 _Image output(input, input.width * 2, input.height * 2);
  383.                 for (int x = output.width - 2, xx = x>>1; x >= 0; x -= 2, --xx)
  384.                         for (int y = output.height - 2, yy = y>>1; y >= 0; y -= 2, --yy) {
  385.                                 const _Color@
  386.                                         S = input.Get(xx-1, yy-1),
  387.                                         T = input.Get(xx, yy-1),
  388.                                         U = input.Get(xx+1, yy-1),
  389.                                         V = input.Get(xx-1, yy),
  390.                                         C = input.Get(xx,yy),
  391.                                         W = input.Get(xx+1, yy),
  392.                                         X = input.Get(xx-1, yy+1),
  393.                                         Y = input.Get(xx, yy+1),
  394.                                         Z = input.Get(xx+1, yy+1);
  395.                                 const _Color@ result; //needs to be a separate variable to fix an angelscript bug
  396.                                 @result = (V==S&&S==T) ? S : C; output.Set(x,y, result);
  397.                                 @result = (T==U&&U==W) ? U : C; output.Set(x+1,y, result);
  398.                                 @result = (V==X&&X==Y) ? X : C; output.Set(x,y+1, result);
  399.                                 @result = (W==Z&&Z==Y) ? Z : C; output.Set(x+1,y+1, result);
  400.                         }
  401.                 return output;
  402.         }
  403.        
  404.         jjPALCOLOR _blendColors(const jjPALCOLOR &in a, const jjPALCOLOR &in b, const float balance = 0.5f) {
  405.                 if (a == b)
  406.                         return a;
  407.                 if (balance == 0.5f) {
  408.                         return jjPALCOLOR(
  409.                                 (uint(a.red) + uint(b.red)) >> 1,
  410.                                 (uint(a.green) + uint(b.green)) >> 1,
  411.                                 (uint(a.blue) + uint(b.blue)) >> 1
  412.                         );
  413.                 } else {
  414.                         const float opposite = 1.f - balance;
  415.                         return jjPALCOLOR(
  416.                                 uint8(balance * a.red + opposite * b.red),
  417.                                 uint8(balance * a.green + opposite * b.green),
  418.                                 uint8(balance * a.blue + opposite * b.blue)
  419.                         );
  420.                 }
  421.         }
  422.  
  423.         int _saiGetResult1(const jjPALCOLOR &in A, const jjPALCOLOR &in B, const jjPALCOLOR &in C, const jjPALCOLOR &in D, const jjPALCOLOR &in E)
  424.         {
  425.                 int x = 0;
  426.                 int y = 0;
  427.                 int r = 0;
  428.                 if (A == C) x+=1; else if (B == C) y+=1;
  429.                 if (A == D) x+=1; else if (B == D) y+=1;
  430.                 if (x <= 1) r+=1;
  431.                 if (y <= 1) r-=1;
  432.                 return r;
  433.         }
  434.  
  435.         int _saiGetResult2(const jjPALCOLOR &in A, const jjPALCOLOR &in B, const jjPALCOLOR &in C, const jjPALCOLOR &in D, const jjPALCOLOR &in E)
  436.         {
  437.                 int x = 0;
  438.                 int y = 0;
  439.                 int r = 0;
  440.                 if (A == C) x+=1; else if (B == C) y+=1;
  441.                 if (A == D) x+=1; else if (B == D) y+=1;
  442.                 if (x <= 1) r-=1;
  443.                 if (y <= 1) r+=1;
  444.                 return r;
  445.         }
  446.  
  447.  
  448.         int _saiGetResult(const jjPALCOLOR &in A, const jjPALCOLOR &in B, const jjPALCOLOR &in C, const jjPALCOLOR &in D)
  449.         {
  450.                 int x = 0;
  451.                 int y = 0;
  452.                 int r = 0;
  453.                 if (A == C) x+=1; else if (B == C) y+=1;
  454.                 if (A == D) x+=1; else if (B == D) y+=1;
  455.                 if (x <= 1) r+=1;
  456.                 if (y <= 1) r-=1;
  457.                 return r;
  458.         }
  459.        
  460.         _Image@ _2xSAI(const _Image &in input) {
  461.                 const _Color black(0);
  462.                 _Image@ output = _nearestNeighbor(input, input.width*2, input.height*2);
  463.                 for (int x = output.width - 2, xx = x>>1; x >= 0; x -= 2, --xx)
  464.                         for (int y = output.height - 2, yy = y>>1; y >= 0; y -= 2, --yy) {
  465.                                 //---------------------------------------
  466. // Map of the pixels:                    I|E F|J
  467. //                                       G|A B|K
  468. //                                       H|C D|L
  469. //                                       M|N O|P
  470.                                 const jjPALCOLOR
  471.                                         I = input.Get(xx-1, yy-1).color,
  472.                                         E = input.Get(xx+0, yy-1).color,
  473.                                         F = input.Get(xx+1, yy-1).color,
  474.                                         J = input.Get(xx+2, yy-1).color,
  475.                                         G = input.Get(xx-1, yy+0).color,
  476.                                         A = input.Get(xx+0, yy+0).color,
  477.                                         B = input.Get(xx+1, yy+0).color,
  478.                                         K = input.Get(xx+2, yy+0).color,
  479.                                         H = input.Get(xx-1, yy+1).color,
  480.                                         C = input.Get(xx+0, yy+1).color,
  481.                                         D = input.Get(xx+1, yy+1).color,
  482.                                         L = input.Get(xx+2, yy+1).color,
  483.                                         M = input.Get(xx-1, yy+2).color,
  484.                                         N = input.Get(xx+0, yy+2).color,
  485.                                         O = input.Get(xx+1, yy+2).color,
  486.                                         P = input.Get(xx+2, yy+2).color;
  487.                                 jjPALCOLOR product, product1, product2;
  488.                                
  489.                                 if ((A == D) && (B != C)) {
  490.                                    if ( ((A == E) && (B == L)) ||
  491.                                                 ((A == C) && (A == F) && (B != E) && (B == J)) )
  492.                                           product = A;
  493.                                    else
  494.                                           product = _blendColors(A, B);
  495.  
  496.                                    if (((A == G) && (C == O)) ||
  497.                                            ((A == B) && (A == H) && (G != C) && (C == M)) )
  498.                                           product1 = A;
  499.                                    else
  500.                                           product1 = _blendColors(A, C);
  501.                                    product2 = A;
  502.                                 } else if ((B == C) && (A != D)) {
  503.                                    if (((B == F) && (A == H)) ||
  504.                                            ((B == E) && (B == D) && (A != F) && (A == I)) )
  505.                                           product = B;
  506.                                    else
  507.                                           product = _blendColors(A, B);
  508.  
  509.                                    if (((C == H) && (A == F)) ||
  510.                                            ((C == G) && (C == D) && (A != H) && (A == I)) )
  511.                                           product1 = C;
  512.                                    else
  513.                                           product1 = _blendColors(A, C);
  514.                                    product2 = B;
  515.                                 } else if ((A == D) && (B == C)) {
  516.                                    if (A == B)
  517.                                    {
  518.                                           product = A;
  519.                                           product1 = A;
  520.                                           product2 = A;
  521.                                    } else {
  522.                                           int r = 0;
  523.                                           product1 = _blendColors(A, C);
  524.                                           product = _blendColors(A, B);
  525.  
  526.                                           r += _saiGetResult1(A, B, G, E, I);
  527.                                           r += _saiGetResult2(B, A, K, F, J);
  528.                                           r += _saiGetResult2(B, A, H, N, M);
  529.                                           r += _saiGetResult1(A, B, L, O, P);
  530.  
  531.                                           if (r > 0)
  532.                                                   product2 = A;
  533.                                           else if (r < 0)
  534.                                                   product2 = B;
  535.                                           else
  536.                                                   product2 = _blendColors(_blendColors(A, B), _blendColors(C, D));
  537.                                    }
  538.                                 } else {
  539.                                    product2 = _blendColors(_blendColors(A, B), _blendColors(C, D));
  540.  
  541.                                    if ((A == C) && (A == F) && (B != E) && (B == J))
  542.                                           product = A;
  543.                                    else
  544.                                    if ((B == E) && (B == D) && (A != F) && (A == I))
  545.                                           product = B;
  546.                                    else
  547.                                           product = _blendColors(A, B);
  548.  
  549.                                    if ((A == B) && (A == H) && (G != C) && (C == M))
  550.                                           product1 = A;
  551.                                    else
  552.                                    if ((C == G) && (C == D) && (A != H) && (A == I))
  553.                                           product1 = C;
  554.                                    else
  555.                                           product1 = _blendColors(A, C);
  556.                                 }
  557.                                        
  558.                                 output.Set(x,y, A);
  559.                                 output.Set(x+1,y, product);
  560.                                 output.Set(x,y+1, product1);
  561.                                 output.Set(x+1,y+1, product2);
  562.                         }
  563.                 return output;
  564.         }
  565.        
  566.         _Image@ _scale_2xSaI(const _Image &in input, uint width, uint height) {
  567.                 _Image output(input, width, height);
  568.                 for (int x = width - 1; x >= 0; --x) {
  569.                         const float
  570.                                 xx = float(x) * input.width / width,
  571.                                 RX = 1 - (floor(xx) + 1 - xx);
  572.                         for (int y = height - 1; y >= 0; --y) {
  573.                                 const float
  574.                                         yy = float(y) * input.height / height,
  575.                                         RY = 1 - (floor(yy) + 1 - yy);
  576.                                 const jjPALCOLOR
  577.                                         A = input.Get(int(xx + 0), int(yy + 0)).color,
  578.                                         B = input.Get(int(xx + 1), int(yy + 0)).color,
  579.                                         C = input.Get(int(xx + 0), int(yy + 1)).color,
  580.                                         D = input.Get(int(xx + 1), int(yy + 1)).color,
  581.                                         E = input.Get(int(xx + 0), int(yy - 1)).color,
  582.                                         F = input.Get(int(xx + 1), int(yy - 1)).color,
  583.                                         G = input.Get(int(xx - 1), int(yy + 0)).color,
  584.                                         H = input.Get(int(xx - 1), int(yy + 1)).color,
  585.                                         I = input.Get(int(xx + 2), int(yy + 0)).color,
  586.                                         J = input.Get(int(xx + 2), int(yy + 1)).color,
  587.                                         K = input.Get(int(xx + 0), int(yy + 2)).color,
  588.                                         L = input.Get(int(xx + 1), int(yy + 2)).color;
  589.                                
  590.                                 jjPALCOLOR result;
  591. /*0*/                   if (A == B && C == D && A == C)
  592.                                         result = A;
  593.                                 else {
  594.                                         const float
  595.                                                 FX = (RX / 2) + 0.25f,
  596.                                                 FY = (RY / 2) + 0.25f;
  597. /*1*/                           if (A == D && B != C) {
  598.                                                 if (RY <= FX && A == J && A != E) // close to B
  599.                                                         result = _blendColors(B, A, FX - RY);
  600.                                                 else if (RY >= FX && A == G && A != L) // close to C
  601.                                                         result = _blendColors(C, A, RY - FX);
  602.                                                 else if (RX >= FY && A == E && A != J) // close to
  603.                                                         result = _blendColors(B, A, RX - FY);
  604.                                                 else if (RX <= FY && A == L && A != G) // close to C
  605.                                                         result = _blendColors(C, A, FY - RX);
  606.                                                 else if (RY >= RX) // close to C
  607.                                                         result = _blendColors(C, A, RY - RX);
  608.                                                 else //if (RY <= RX) // close to B
  609.                                                         result = _blendColors(B, A, RX - RY);
  610. /*2*/                           } else if (B == C && A != D) {
  611.                                                 const float
  612.                                                         RXO = (1.f - RX),
  613.                                                         RYO = (1.f - RY);
  614.                                                 if (RYO >= FX && B == H && B != F) // close to A
  615.                                                         result = _blendColors(A, B, RYO - FX);
  616.                                                 else if (RYO <= FX && B == I && B != K) // close to D
  617.                                                         result = _blendColors(D, B, FX - RYO);
  618.                                                 else if (RXO >= FY && B == F && B != H) // close to A
  619.                                                         result = _blendColors(A, B, RXO - FY);
  620.                                                 else if (RXO <= FY && B == K && B != I) // close to D
  621.                                                         result = _blendColors(D, B, FY - RXO);
  622.                                                 else if (RYO >= RX) // close to A
  623.                                                         result = _blendColors(A, B, RYO - RX);
  624.                                                 else //if (RYO <= RX) // close to D
  625.                                                         result = _blendColors(D, B, RX - RYO);
  626. /*3*/                           } else
  627.                                                 result = _blendColors(_blendColors(D, C, RX), _blendColors(B, A, RX), RY); //just use basic bilinear interpolation
  628.                                 }
  629.                                 output.Set(x,y, result);
  630.                         }
  631.                 }
  632.                 return output;
  633.         }
  634. }