Building a custom .dll file to use with RPG Maker...

Started by Heretic86, June 03, 2016, 05:55:07 pm

Previous topic - Next topic

Heretic86

Since I'm currently stuck on my Light Script, I thought I would expand my skillset and do it in a way that helps everyone interested.  Im going with C++ for this.  Getting a compiler was a pain, but I finally got one.  Visual C++ 2015.

So what Ive done so far is this.  New Win32 project, dll, and empty project.

Header.h
#define DLLEXPORT __declspec(dllexport)

extern "C" DLLEXPORT int hello(int);


Source.cpp
#include "header.h"

int hello(int arg)
{
arg++;
return arg;
}


RPG Maker Script (Ruby):
class Game_Character
  Hello =  Win32API.new("rotate.dll", "hello", "I", "I")
 
  def hello(arg)
    print Hello.call(arg)
  end
end


Event Move Route -> Script:
$>script: hello(5)

---

This is as basic as it gets.

Im completely unfamiliar with C++ headers.  So if my next step is to interact with a Bitmap, where do I go from there?  Also assuming that since Im working with Transparency that is had from .png files, the term "Bitmap" is a bit misleading.

I tried looking at a few examples online, but it doesnt make sense yet.  Im stuck at exporting the function in DLLEXPORT, but basically I'll make a function that does image rotation using some of the prebuilt methods, (GDI?) and return the Image.  Or, do I need to use pointers and make changes to the memory addresses on the fly?  Wouldnt that affect the bitmap stored in RPG::Cache?

extern "C" DLLEXPORT HBITMAP rotateBitmap(HBITMAP, float)

I know I need another header in there so I can declare what a HBITMAP is...

What is my next step?

*feels very stupid*
Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)

KK20

It's pretty tricky if you don't have any resources to rely on. Perhaps you should take a look at your old topic again?
http://forum.chaos-project.com/index.php/topic,14127.0.html

The main important thing to get out of it is the bitmap structure:

typedef struct {
    DWORD flags;
    DWORD klass;
    void (*dmark) (void*);
    void (*dfree) (void*);
    BYTE *data; //B = 0, G = 1, R = 2, A = 3
} RGSSCOLOR;

typedef struct{
    DWORD unk1;
    DWORD unk2;
    BITMAPINFOHEADER *infoheader;
    RGSSCOLOR *firstRow;
    RGBQUAD *lastRow;
} RGSSBMINFO;

typedef struct{
    DWORD unk1;
    DWORD unk2;
    RGSSBMINFO *bminfo;
} BITMAPSTRUCT;

typedef struct{
    DWORD flags;
    DWORD klass;
    void (*dmark) (void*);
    void (*dfree) (void*);
    BITMAPSTRUCT *bm;
} RGSSBITMAP;

/*==========================================================================
** BlackAndWhite
Input:
- object : Address to the Bitmap object (i.e. bitmap.object_id )
----------------------------------------------------------------------------
Turns a bitmap image into a grayscale version. Uses the basic
implementation of grayscale conversion, that is (R+G+B)/3 applied to
each pixel's RGB values.
Kept to explain how bitmap manipulation works in DLLs.
==========================================================================*/

extern "C" __declspec (dllexport) bool BlackAndWhite(long object){
    RGSSBMINFO *bitmap = ((RGSSBITMAP*) (object<<1)) -> bm -> bminfo;
    DWORD rowsize;
    DWORD width, height;
    LPBYTE row;
    long x, y;
    int shade;
    if(!bitmap) return false;
    width = bitmap -> infoheader -> biWidth;
    height = bitmap -> infoheader -> biHeight;
    rowsize = width * 4;
    row = (LPBYTE) (bitmap -> firstRow);
    for ( y = 0; y < (int) height; y++) {
        LPBYTE thisrow = row;
        for ( x = 0; x < (int) width; x++) {
            shade = ((thisrow[2] + thisrow[1] + thisrow[0]) / 3);
            thisrow[2] = shade;
            thisrow[1] = shade;
            thisrow[0] = shade;
            thisrow += 4;
        }
        row -= rowsize;
    }
    return true;
}

To use the code above, your ruby would look like

s = Sprite.new
s.bitmap = Bitmap.new #draw stuff to it or use RPG::Cache and load a graphic
dll = WinAPI32.new('NAME.dll', 'BlackAndWhite', 'L', 'I')
dll.call(s.bitmap.object_id)


I don't know if converting the Bitmap struct to something else, using a graphic library's method, and converting it back is possible (leaving that to Blizz since my C/++ is not that great). You're probably just going to have to suck it up and write the algorithm yourself.

And yeah, you're going to need to pass in both a source and destination bitmap as your arguments if you don't want to affect the cached bitmap.

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

Heretic86

Im fine with writing my own algorithm, but Im pretty sure one already exists in TransformRotate from a few mentions of stuff I've seen online using Graphics Device Interface for a hardware transform for speed.  Mostly, learning experience.

Now, question:

extern "C" __declspec (dllexport) bool BlackAndWhite(long object){
    RGSSBMINFO *bitmap = ((RGSSBITMAP*) (object<<1)) -> bm -> bminfo;

...


Exporting dll functions can be done at the same time as the function declaration?  Returning a bool?  Hmm, maybe it would be better to have void function_name(*bitmap, angle), then instead of returning a bitmap, just access the memory addresses via pointers?  Im not sure cuz I dont know anything about how the GDI works, or if it can be handled in the same memory space (RAM vs GPU Ram).  Here's what Im thinkin...

# Shallow Copy of Image
light_bmp = RPG::Cache.light(@light.file_name, @light.hue).clone
# Rotate Image if needed
light_bmp = Rotate.call(light_bmp, @light.angle) if @light.angle != 0
# Width and Height (changes with Rotation)
w, h = light_bmp.width, light_bmp.height

...

# Draw Image into the Shadowmap Sprite to create a Light Hole in Shadow
@shadowmap.bitmap.stretch_blt(rect, light_bmp, light_bmp.rect, opacity)


Think that could possibly work?
Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)

KK20

If your rotate method is a void function, the light_bmp = is impossible. You are just accessing the bitmap color data directly using pointers. There's nothing to return as a result. The bool in my example is just there for cases where if you want to error check: TRUE if good, FALSE otherwise.

You have to pass in the Bitmap#object_id. This is the address to the bitmap object in memory.

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

Heretic86

Ooop, yep, thats true, void wouldnt give any value back.  Wasnt sure if it should or shouldnt provide a return value...
Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)

orochii

I love when you people talk about these things, it's the reason why I'm in this community <3.

This feels me with determination.

Oh, just don't mind me, please continue with your conversation.

Blizzard

Quote from: Heretic86 on June 03, 2016, 08:52:56 pm
extern "C" __declspec (dllexport) bool BlackAndWhite(long object){
    RGSSBMINFO *bitmap = ((RGSSBITMAP*) (object<<1)) -> bm -> bminfo;

...


Exporting dll functions can be done at the same time as the function declaration?  Returning a bool?  Hmm, maybe it would be better to have void function_name(*bitmap, angle), then instead of returning a bitmap, just access the memory addresses via pointers?  Im not sure cuz I dont know anything about how the GDI works, or if it can be handled in the same memory space (RAM vs GPU Ram).  Here's what Im thinkin...


I just want to add that exporting in headers makes the most sense if you are building a DLL that's to be used by other C code. It's easier to just include a header in a depending library than write the function definition all over again. Since Ruby can't do that, you don't need that there and can go without headers. Also, Ruby can only use C DLLs, not C++ DLLs.




As for the format of the bitmap, it's bottom-row-first and the pixel format is BGRA LSB. You don't have to do decode that PNG in the C code, RGSS does that for you. That's why you load the Bitmap and then pass Bitmap#object_id to the DLL.

Feel free to take a look at how I implemented some Bitmap functionality in my XPA_Window implementation. It's similar to Bitmap#stretch_blt, but it overwrites the pixels and does linear interpolation rather than nearest neighbor interpolation. This is one of the important pieces:


unsigned char* srcData = (unsigned char*)srcBitmap->firstRow; // previously declared somewhere
...
ctl = &srcData[(_x0s[x] - _y0s[y] * srcWidth) * BPP];


If the top row was the first row in the data, the code should look like this:


ctl = &srcData[(_x0s[x] + _y0s[y] * srcWidth) * BPP];


But it's not. So I have to keep going backwards through the rows. The rows themselves are ok though.
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

ForeverZer0

A couple of years ago I made a dll that performs all sorts of manipulations on bitmaps. Never did complete it totally, and it is written in C# with .NET Framework, but much of the methods could be easily translated to another language. You can at least see examples of how the pointers to the bitmaps are handled, etc.

https://sourceforge.net/projects/rpgnet/
I am done scripting for RMXP. I will likely not offer support for even my own scripts anymore, but feel free to ask on the forum, there are plenty of other talented scripters that can help you.

Heretic86

June 05, 2016, 06:57:30 pm #8 Last Edit: June 05, 2016, 06:59:17 pm by Heretic86
Okay, Im feelin REALLY dumb.

Using C++, and struggling on the basics, with Pointers.

Ruby Code:
class Game_Character
  # 4 Args - DLL File, DLL Function, Send Type, Return Type (ILPV - Int Long Pointer Void)
  Sub =  Win32API.new("RMDLL.dll", "sub", "P", "")
  // arg : C++ int
  def sub(arg)
    Sub.call(arg)
    print "after DLL ", arg
  end
end


C++ Code
#define DLLEXPORT __declspec(dllexport)
DLLEXPORT

extern "C" __declspec (dllexport) void sub(int *arg)
{
  *arg--;
}


I think I get the concept of Pointers where & is memory address and * is for value held in that memory, but apparently not.  In the Win32API, using "P" says Pointer and "" for returning void / null / nil, wouldnt the dll function modify the value held in the address of RMXP memory so after I make any modifications to it via the dll, why doesnt it show up in RMXP?  Im apparently not getting the correct differences between "int* arg", "int *arg", and "*arg--"...  What is malfunctioning in my melting brain pan?

Also tried some stuff using strings, returning a pointer from the dll with return std::string "Hello World"; and getting the string with some funky characters on the left after .unpack("A*) which only sort of seems to work because I cant clone or copy the value to mess with the string later.  What is going on there?

(Also, asking these questions as I am sure anyone else interested in making a custom dll will probably have the same issues, so please keep in mind that more people than just us are looking at this when answering...)
Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)

Heretic86

June 10, 2016, 07:58:43 pm #9 Last Edit: June 10, 2016, 07:59:44 pm by Heretic86
Someone want to explain why this doesnt work?

char *p[20];
extern "C" __declspec (dllexport) void mk_str(char *p)
{
char txt[20];
txt[0] = 'H';
txt[1] = 'i';
p = txt;
}


class Game_Character
  Mk_Str = Win32API.new("RMDLL.dll", "mk_str", "P", "")

  def mk_str()
    @dll_var = "Foo"
    print "Before is ", @dll_var, "\nObject ID is ", @dll_var.object_id
    Mk_Str.call(@dll_var.object_id)
    print "After change is ", @dll_var
  end
end


Is anyone still following this thread?
Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)

KK20

My guess is by doing @dll_var.object_id, you're returning the address to the String object. And by String object, I mean Ruby's class String, not the text itself. If you looked at some examples of Win32API being used in other people's scripts, you would have answered the question yourself.

Like from Custom Resolution or XPA Tilemap:

ini = Win32API.new('kernel32', 'GetPrivateProfileString','PPPPLP', 'L')
title = "\0" * 256
ini.call('Game', 'Title', '', title, 256, '.\\Game.ini')
title.delete!("\0")
@window = Win32API.new('user32', 'FindWindow', 'PP', 'I').call('RGSS Player', title)

A string is already a char array/pointer. There's no need to object_id it.

Also the char *p[20] is completely unnecessary. That's just basic C stuff. It would also be better if you just made the changes to your argument directly rather than creating another char pointer/array.

Quote(Also, asking these questions as I am sure anyone else interested in making a custom dll will probably have the same issues, so please keep in mind that more people than just us are looking at this when answering...)

This statement is redundant as this is what we have been doing this entire time. We can't dumb this down anymore for everyone to be able to make a DLL. If anyone can't follow along to what's being said here, you shouldn't be making a DLL in the first place.
QuoteIs anyone still following this thread?

Work sucks. Weekends are the best.

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

Blizzard

Agreed, char*p[20] is redundant.

Your code isn't working, because you are filling the data into a local string and then just overwriting the pointer you got from RMXP. You would have to use memcpy to make this work rather that p = txt. Also, you are missing the terminator. char p[20] does not initialize the memory, only allocate it. char p[20] = { 0 } would be the correct way to do it.

That being said, KK20 is right and you can directly manipulate the string you got from RMXP. And since char is A basic type, you don't need to pass object_id, but just the string itself like in his example.
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

Heretic86

June 11, 2016, 04:10:26 am #12 Last Edit: June 11, 2016, 04:37:35 am by Heretic86
One would figure that for the number of attempts I've made to get this to work, I should have at least stumbled on a solution by now even if I didnt understand it.  This is why I didnt continue to learn the last time too.  Concept of a simple "Hello World".  Here DLL, this is my ruby var, lets change the text to "Hello World" and I cant even do something as simple that.  If I cant do that, I'll never be able to modify anything else using dll files. 

Fuck I feel retarded.  Got shit on by my neighbor.  2 full weeks of dog sitting full time 24 / 7, watering and mowing lawn and house plants for a whopping 60 bucks.  Doesnt help the mood at all.  Im seriously about this close to giving up on dll files and C / C++ completely.

Sorry, Im just in a really bad mood after this shit with my snake of a neighbor.
Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)

Blizzard

Relax. You can always pick it up again tomorrow or later. You're smart, you'll eventually get it. It's just a matter of not giving up.
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

Heretic86

Im still screwing around with this...

The structs and what not that KK20 pointed back to are somewhat confusitating.  One thing I do need to do is change the size of a bitmap passed to a dll, but I dont see much in the way of the structs and ->biWidth and ->biHeight arent doing it...  Any suggestions on how to change the size of an RGSS Bitmap?
Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)

Blizzard

Better don't do that in a DLL, because you don't know how the data was allocated in the first place. Your best bet is to create a target Bitmap with the desired size and pass it to the DLL as well. Then just do all your operations in such as way that you write them to the target Bitmap rather than try to modify the original. I don't think you can resize Bitmap instances in RGSS either so this imposes itself as a valid solution.
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

Heretic86

*downgrades IQ*

Okay, how the hell do I get a float / double in as an argument?
Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)

Blizzard

If I remember correctly, you use F instead of L in the arg listing.
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

KK20

F is not supported. My guess is you will have to pass in an array and modify it in the DLL.
Quote from: ThallionDarkshine on May 03, 2014, 07:13:21 pm
If anyone is interested, to pass arrays to a dll, you simply create a ruby array, and call the pack method to pack it into something that can be passed to a win32api method. Ex.
Code: ruby
Array.new(10) { 0.0 }.pack("f*")

When receiving an array value in the dll, you take it in as a pointer to whatever type you are sending in. For example, for the array from the ruby example, the cpp type would be
Code: cpp
float*



I think this is what you do:
Code: DLL

extern "C" __declspec (dllexport) void method(float * var)
{
var[0] = 2.0 * var[0];
}


Code: Ruby

myDLL = Win32API.new('dll','method','p','')
var = [1.0]
result = myDLL.call(var.pack("F*"))

print result.unpack("F*")[0] #should be 2.0

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

Heretic86

@Blizz or Satan or Grapist or whatever the hell name you come up with this week...  :D

Using "F" for Float didnt work.  Some googling says there are only 4 types of args for the Win32api calls:
I - int
L - long
P - pointer (usually string?)
V - void

KK20's stuff did work however.  Now at least I can monitor some values from inside the dll file.  Im passing an extra argument for a MessageBoxA that displays values I want to see from inside the DLL file, which is how I was finding I had a loss of data thru the float argument.

Ruby
showMsg = (Input.press?(Input::SHIFT)) ? 1 : 0


C++
  if (showMsg == 1)
    {
      std::string myStr = std::to_string(angle);
      MessageBoxA(hWnd, myStr.c_str(), "DLL Message", MB_OK);
    }


Then I got fucking LPCWSTR crap from standard MessageBox and learned about MessageBoxA by stumbling upon it.  I officially hate C++ with a passion.  I think once I am done doing with it the stuff I need, Im gonna put it under a pile of dead babies in a trash can and burn it.
Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)

Heretic86

June 22, 2016, 05:37:11 am #20 Last Edit: June 22, 2016, 07:13:51 pm by Heretic86
Yay!  I got garbage to be displayed, then crash!  Well, at least I got somewhere...  :facepalm:

--

Okay, source code so far.  It has some nasty problems tho.

dllmain.cpp
Spoiler: ShowHide
#include <cstdlib>
#include <ctime>
#include <ctype.h>
#include <math.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <windows.h>

typedef union {
unsigned int pixel;
struct {
unsigned char blue;
unsigned char green;
unsigned char red;
unsigned char alpha;
};
} RGSSRGBA;

typedef struct {
DWORD flags;
DWORD class_type;
void(*dmark) (void*);
void(*dfree) (void*);
double *data; //red is index 1, green is index 2, blue 3, alpha 0
} RGSSCOLOR;

typedef struct {
DWORD unk1;
DWORD unk2;
BITMAPINFOHEADER *infoheader;
RGSSRGBA *firstRow;
RGSSRGBA *lastRow;
} RGSSBMINFO;

typedef struct {
DWORD unk1;
DWORD unk2;
RGSSBMINFO *bminfo;
} BITMAPSTRUCT;

typedef struct {
DWORD flags;
DWORD class_type;
void(*dmark) (void*);
void(*dfree) (void*);
BITMAPSTRUCT *bm;
} RGSSBITMAP;

# define M_PI           3.14159265358979323846  /* pi */

//===============================================================================
// * RotateRGSSBitmap
//  - Rotates Bitmaps in Game Memory
//        src_object : Object ID of RPG::Bitmap (bmp.object_id)
//     canvas_object : Object ID of Canvas to be drawn to - RPG::Bitmap
//             angle : Angle to rotate the Bitmap to in Degrees
//           showMsg : send a 1 to this Arg for a MessageBox
//===============================================================================
// Definately a work in progress.

extern "C" _declspec (dllexport) int RotateRGSSBitmap(unsigned long src_object, unsigned long canvas_object, float *pAngle, int showMsg = 0)
{
  // Get the Pointers to both Bitmaps for the Light Source and Canvas object to draw on
RGSSBMINFO *lightSrc = ((RGSSBITMAP*)(src_object << 1))->bm->bminfo;
RGSSBMINFO *lightCanvas = ((RGSSBITMAP*)(canvas_object << 1))->bm->bminfo;
// Quit processing if there is no Light Source RGSS Bitmap
if (!lightSrc || !lightCanvas) return 0;
// Get the angle value from packed pointer in Arg
double angle = pAngle[0];
// RGSS specific local variables used to transfer from Source to Canvas
RGSSRGBA *sourceData, *canvasData;
// Local Variables for determining rotation
int x, y, widthSrc, heightSrc, widthCanvas, heightCanvas;
// Width and Height of Light Source and Canvas RGSS Bitmaps
widthSrc = lightSrc->infoheader->biWidth;
heightSrc = lightSrc->infoheader->biHeight;
widthCanvas = lightCanvas->infoheader->biWidth;
heightCanvas = lightCanvas->infoheader->biHeight;
// Use Center of Canvas as center of rotation
float cX = (float)widthCanvas / 2;
float cY = (float)heightCanvas / 2;

// Change the angle in Degrees to Radians
double radianAngle = (M_PI * angle) / 180;
// Determine Sine and Cosine of the Angle
double cosAngle = (double)cos(radianAngle);
double sinAngle = (double)sin(radianAngle);
// Bitmap RGBA starting at last Pixel which holds 4 values, RGBA
sourceData = lightSrc->lastRow;
canvasData = lightCanvas->lastRow;
// Points to specific Pixel Data
RGSSRGBA *sourceRGBA, *canvasRGBA;
// Holds Position of X and Y to transfer the data to
double posX, posY;

// Iterate through the Source Pixels and draw them on to the Canvas
for (y = 0; y < heightSrc; y++)
{
for (x = 0; x < widthSrc; x++)
{
// Determine position on the Canvas Bitmap to draw
posX = x * cosAngle + y * sinAngle;
posY = x * sinAngle - y * cosAngle;
// Recenter (not working right)
posX += cX;
posY += cY;
// I dont have a clue what this does, I just copied and pasted
sourceRGBA = sourceData + y + (x * widthSrc);
canvasRGBA = canvasData + (int)posY + ((int)posX * widthCanvas);
// If new position on canvas is actually within the canvas
if (posY >= 0 && posY <= heightCanvas && posX >= 0 && posX <= widthCanvas)
{
// Transfer Data from the Source Image to the Canvas
canvasRGBA->red = sourceRGBA->red;
canvasRGBA->green = sourceRGBA->green;
canvasRGBA->blue = sourceRGBA->blue;
canvasRGBA->alpha = sourceRGBA->alpha;
}
}
}

// Debugging to display data inside DLL
if (showMsg == 1)
{
// Change the variable name from "angle" here for displaying data
// Remember to push the SHIFT key to use this Message Box
std::string myStr = std::to_string(angle);
MessageBoxA(NULL, myStr.c_str(), "DLL Message", MB_OK);
}

// Return Successful Rotation
return 1;
}


When rotated, I get a TON of garbage when angles are not 90s.  The box looking thing is supposed to be SOLID without all the noise.



First - CRASH - when the angle hits exactly 270.0 degrees, game just stops.  Non informative errors.  I figure division by 0 somewhere, but not sure exactly where.  I dont use the dll at all when the angle is 0.

Next, I cant quite figure out the math to center the image correctly.

Next, totally looks like shit.  Cant use for quality production.  I know there are better algorithms out there, but hey, its as far as I have gotten so far.  Im still quite confusitated by so much crap in c++ like, what the hell is "   void(*dmark) (void*); void(*dfree) (void*); "

Anyone wanna take a crack at making things look better?
Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)

Heretic86

June 22, 2016, 07:33:40 am #21 Last Edit: June 22, 2016, 07:53:42 am by Heretic86
You'll need the Light Script also where I plan to use the DLL file.  Too much code for one post and I will remove this post later due to code version being in an unstable Prototype Stage.

STANDALONE LIGHT SCRIPT - PROTOTYPE - NOT STABLE - NOT LEGAL FOR RELEASE OR DISTRIBUTION


Legal crap aside, here is how to use this so far.

INSTRUCTIONS
Spoiler: ShowHide
First, use some sort of Image Editing program to create a PNG with an Alpha channel.  A simple gradient circle will do.  It doesnt have to be fancy.  Then make a new folder in RPG Maker XP project Graphics and call it "Lights".  Put your Light Image in there.  The default name I look for is "Light1.png".

THis is the one I used if you are too lazy to make your own:
[spoiler]
http://downloads.chaos-project.com/heretic86/Light1.png


Next, make an Event, and give it a Comment in the first 10 lines or so that reads "\light[]" without the quotes.  You can put a custom file name between the two [] characters to give each event its own individual Lightsource Image.  \light[torch.png] for example.

Now, we need some shadows for the Lights to cut thru.  There are two ways to do this.  Indoor and Outdoor, or set the Time to Night.
- Indoor Switch -> Just set Switch 75 to ON

Once that SWITCH is turned on, the screen should be much darker, but still visible.  If you have a Light (Event with \light[] comment) on the screen, then the Light will cut out a part of the shadow that covers the screen.

Now, to kick in the DLL, set the ANGLE property of your event to anything BUT zero:
$game_map.events[3].light.angle = 30;

You might wanna use a parallel process to just keep adding 1 to the angle.  It defaults to 0.[/spoiler]

Script has some documentation but is not complete at this point.

I also have a ton of DEBUG stuff in there.  When Bitmaps are rotated, I am drawing a border around them so I can see the dimensions and position of the Bitmap that I use for creating the Light Sources.  Ignore the border.  Rotated Image is really looks like crap and I need advice.

If a Demo is needed, I'll upload one so I can get this stupid DLL part of this script out of the way.  10 edits later, I think Im done editing this post.  :P
Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)

KK20

Nice triple bro

Providing a demo would be better (and faster) for us. But seeing those screenshots, I don't really think there's any merit in even trying it.
I just started looking into making a DLL yesterday and got moderate progress using an abandoned project that I believe you looked at:
https://github.com/scanti/RPG-Maker-Bitmap-Functions
...which I just now realized Thallion worked on this.

Going to look into centering the axis of rotation and transferring the pixel data to a secondary bitmap and see where that takes me.

Regarding the "garbage", your C++ is looping through the source bitmap pixels when it should be the destination bitmap. That's why it's producing all these holes in your image.

For the crashes, it might be you are requesting pixel data for a coordinate outside of the bitmap. Maybe try moving the setting of sourceRGBA and canvasRGBA inside the posX/Y if statement.

For the weird things inside the structs, ignore them. You can think of the bitmap objects you are passing into the DLL as an array of various data. Once you do
((RGSSBITMAP*)(src_object << 1))

it kinda looks like
[flags, class_type, unknown_thing, another_unknown_thing, *bm]

Have you even tried removing the voids and seeing what happens to your DLL? I'm pretty sure it will crash since your *bm would be assigned to the address stored in 'unknown_thing', which should not be holding a BITMAPSTRUCT.

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

Heretic86

I was working on the Demo, just took longer to put together than I expected due to constant interruptions.

http://downloads.chaos-project.com/heretic86/LightDLL.zip

Havent changed any of the source code yet, but I'll try what you suggested. 

Thanks!
Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)

KK20

My Result
Spoiler: ShowHide

DLL:
Spoiler: ShowHide

typedef union {
unsigned int pixel;
struct {
unsigned char blue;
unsigned char green;
unsigned char red;
unsigned char alpha;
};
} RGSSRGBA;

typedef struct {
    DWORD flags;
    DWORD klass;
    void (*dmark) (void*);
    void (*dfree) (void*);
    BYTE *data; //B = 0, G = 1, R = 2, A = 3
} RGSSCOLOR;

typedef struct{
    DWORD unk1;
    DWORD unk2;
    BITMAPINFOHEADER *infoheader;
    RGSSRGBA *firstRow;
    RGSSRGBA *lastRow;
} RGSSBMINFO;

typedef struct{
    DWORD unk1;
    DWORD unk2;
    RGSSBMINFO *bminfo;
} BITMAPSTRUCT;

typedef struct{
    DWORD flags;
    DWORD klass;
    void (*dmark) (void*);
    void (*dfree) (void*);
    BITMAPSTRUCT *bm;
} RGSSBITMAP;

bool DLL_EXPORT Transform33(long bmp, float * matrix)
{
    RGSSBMINFO *bitmap = ((RGSSBITMAP*)(bmp<<1))->bm->bminfo;

    long width, height;
    RGSSRGBA *row;
RGSSRGBA *rowcopy;
    long x, y;
    float nx, ny;
    float cx, cy;
    unsigned char red,green,blue,alpha;

float transx, transy, transw;

    if(!bitmap)
return false;
    width = bitmap->infoheader->biWidth;
    height = bitmap->infoheader->biHeight;
    cx = (float)width / 2 - 0.001;
    cy = (float)height / 2 - 0.001;

rowcopy = new RGSSRGBA[width*height];
    row = bitmap->lastRow;
    memcpy(rowcopy,row,width*height*4);

RGSSRGBA *sourcepos;

    for ( y = 0; y < height; y++)
{
        for ( x = 0; x < width; x++)
{
    nx = x - cx;
    ny = y - cy;

transx=(float)(matrix[0]*nx + matrix[1]*ny + matrix[2]);
transy=(float)(matrix[3]*nx + matrix[4]*ny + matrix[5]);
transw=(float)(matrix[6]*nx + matrix[7]*ny + matrix[8]);

// Dividing by zero is bad

if (transw==0)
transw=(float)0.001;

transx=(transx + cx)/transw;
transy=(transy + cy)/transw;

if (transx<0 || transx>width || transy<0 || transy>height)
{
red=0;
green=0;
blue=0;
alpha=0;
}
else
{
sourcepos = rowcopy + (int)transx + ((int)transy * width);

red = sourcepos->red;
green = sourcepos->green;
blue = sourcepos->blue;
alpha = sourcepos->alpha;
}

row->red = red;
row->green = green;
row->blue = blue;
row->alpha = alpha;

            row++;
        }
    }

delete[]rowcopy;

    return true;
}

Note I still just pass in one bitmap object. I figured it's easier that way and less coding for me in the long run.

Ruby Test (place anywhere above Main, left/right to spin):
Spoiler: ShowHide


def new_bitmap(width, height, sin, cos)
  sin = sin.abs
  cos = cos.abs
 
  w = (width * cos + height * sin).to_i
  h = (width * sin + height * cos).to_i
 
  Bitmap.new(w, h)
end

class Float < Numeric
  def approx
    target = self.round
    return target if (self + 0.00001 >= target && self - 0.00001 <= target)
    return self
  end
end

 

RotateBitmap = Win32API.new('RotateBitmap','Transform33','lp','i')

s = Sprite.new
s.x = 200
s.y = 200
src = RPG::Cache.picture('square')
s.bitmap = Bitmap.new(96,96)
s.bitmap.blt(0, 0, src, Rect.new(0,0,96,96))
angle = 0

while true
  Graphics.update
  Input.update
  s.update
  old_angle = angle
 
  if Input.repeat?(Input::RIGHT)
    angle += 5
  elsif Input.repeat?(Input::LEFT)
    angle -= 5
  end
 
  if old_angle != angle
    angle %= 360
   
    rads = Math::PI * angle / 180
    sin = Math.sin(rads).approx.to_f
    cos = Math.cos(rads).approx.to_f
           
    arr = [cos, -sin, 0.0,
           sin, cos,  0.0,
           0.0, 0.0,  1.0]
           
    s.bitmap.clear
    s.bitmap.dispose
    s.bitmap = new_bitmap(96, 96, sin, cos)
   
    x = s.bitmap.rect.width - 96
    y = s.bitmap.rect.height - 96
   
    s.bitmap.blt(x/2, y/2, src, Rect.new(0,0,96,96))
    s.ox = x/2
    s.oy = y/2
   
    RotateBitmap.call(s.bitmap.object_id, arr.pack("F*"))

  end
 
end



Graphic used:

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

Heretic86

If I were a girl, Id offer to have your babies!

Can I just use your dll and give you credit instead?
Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)

KK20

Also give credit to scanti and TD. I also forgot to test rectangles, so better check that first.

Is there anything in the code you need clarification on?

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

Heretic86

I'll give them credit too.

Rectangles get clipped.  Tried it.
Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)

Blizzard

Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

KK20

Rectangles are fine. Just needed a couple tweaks to the Ruby test to get it working:

def new_bitmap(width, height, sin, cos)
  sin = sin.abs
  cos = cos.abs
 
  w = [(width * cos + height * sin).to_i, width].max
  h = [(width * sin + height * cos).to_i, height].max
 
  Bitmap.new(w, h)
end

class Float < Numeric
  def approx
    target = self.round
    return target if (self + 0.00001 >= target && self - 0.00001 <= target)
    return self
  end
end

 

RotateBitmap = Win32API.new('RotateBitmap','Transform33','lp','i')

s = Sprite.new
s.x = 200
s.y = 200
src = RPG::Cache.picture('rectangle')
bw = src.rect.width
bh = src.rect.height
s.bitmap = Bitmap.new(bw,bh)
s.bitmap.blt(0, 0, src, Rect.new(0,0,bw,bh))
angle = 0

while true
  Graphics.update
  Input.update
  s.update
  old_angle = angle
 
  if Input.repeat?(Input::RIGHT)
    angle += 5
  elsif Input.repeat?(Input::LEFT)
    angle -= 5
  end
 
  if old_angle != angle
    angle %= 360
   
    rads = Math::PI * angle / 180
    sin = Math.sin(rads).approx.to_f
    cos = Math.cos(rads).approx.to_f
           
    arr = [cos, -sin, 0.0,
           sin, cos,  0.0,
           0.0, 0.0,  1.0]
           
    s.bitmap.clear
    s.bitmap.dispose
    s.bitmap = new_bitmap(bw, bh, sin, cos)
   
    x = s.bitmap.rect.width - bw
    y = s.bitmap.rect.height - bh
   
    s.bitmap.blt(x/2, y/2, src, Rect.new(0,0,bw,bh))
    s.ox = x/2
    s.oy = y/2
   
    RotateBitmap.call(s.bitmap.object_id, arr.pack("F*"))

  end
 
end

So using that as a guide, it shouldn't be hard to incorporate it into the script now.

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

Heretic86

June 25, 2016, 08:42:46 am #30 Last Edit: June 25, 2016, 08:45:49 am by Heretic86
Quote from: Blizzard on June 25, 2016, 01:36:52 am
So you were finally able to get it done? :)


Im close.  Very close.  Lot of commenting needs to be done, a few features, cleanup, etc.  Other than a bit of trouble with a "Spotlight" feature, and like KK20 mentioned, Rectangles, its coming along very nicely.  Im fairly certain the DLL itself is done.  I had no choice but to use KK20's code for the DLL so gotta give credit where credit is due for all this help you guys are providing me with.

This looks kind of cool, right?

(Animated Gif, give it a moment...)

Aside from the nightmare I had with that dll stuff, Im now working on tying a couple of scripts together.  I already wrote a Rotate, Zoom, and Pendulum script so Im tying a bunch of features of that script together with the Light stuff so I dont end up rewriting the same code with the same features.

Other than that, it is just a matter of focusing on any other Features that users may want.  Hopefully as user friendly as I can make it!  Best of all, super high compatability, even with other Screen Resolution scripts!  I havent had a chance to test that out but since its a simple sprite that covers the screen to create the whole Light effect, just setting the size of that sprite in the Script Config as a variable should let it be compatible.  I mean, within reason.  If they get rid of the Event classes, not much I can do there.
Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)

Blizzard

Looks pretty good. :)
BTW, in my XPA_Window implementation there is a WriteLinear() method in the DLL which basically does zooming using linear interpolation. Feel free to use it.
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

Heretic86

@Blizz - I doubt Im good enough to do that yet.

DLL still gives me crashes.  KK20 suspects trying to draw outside the bitmap.

I am doing some specific things that I thought would work that always exist when the crash occurs.  For the "spotlight" effect, Im using stretch_blt to put the original source image into a Bitmap Canvas to rotate.  Just creating a Bitmap.new(new_w, new_h) also causes a crash with to blt or stretch.  The unaltered New Bitmap also crashes upon rotation.  "Ordinary" rotating doesnt cause crashes, but stretching does cause random crashes.

KK20rotate.cpp
Spoiler: ShowHide
// KK20Rotate.cpp : Defines the exported functions for the DLL application.
//

#include "stdafx.h"

typedef union {
unsigned int pixel;
struct {
unsigned char blue;
unsigned char green;
unsigned char red;
unsigned char alpha;
};
} RGSSRGBA;

typedef struct {
DWORD flags;
DWORD klass;
void(*dmark) (void*);
void(*dfree) (void*);
BYTE *data; //B = 0, G = 1, R = 2, A = 3
} RGSSCOLOR;

typedef struct {
DWORD unk1;
DWORD unk2;
BITMAPINFOHEADER *infoheader;
RGSSRGBA *firstRow;
RGSSRGBA *lastRow;
} RGSSBMINFO;

typedef struct {
DWORD unk1;
DWORD unk2;
RGSSBMINFO *bminfo;
} BITMAPSTRUCT;

typedef struct {
DWORD flags;
DWORD klass;
void(*dmark) (void*);
void(*dfree) (void*);
BITMAPSTRUCT *bm;
} RGSSBITMAP;

extern "C" _declspec (dllexport) bool RotateRGSSBmp(long bmp, float * matrix)
{
RGSSBMINFO *bitmap = ((RGSSBITMAP*)(bmp << 1))->bm->bminfo;

long width, height;
RGSSRGBA *row;
RGSSRGBA *rowcopy;
long x, y;
float nx, ny;
float cx, cy;
unsigned char red, green, blue, alpha;

float transx, transy, transw;

if (!bitmap)
return false;
width = bitmap->infoheader->biWidth;
height = bitmap->infoheader->biHeight;
cx = (float)width / 2 + 0.0001;
cy = (float)height / 2 + 0.0001;

rowcopy = new RGSSRGBA[width*height];
row = bitmap->lastRow;
memcpy(rowcopy, row, width*height * 4);

RGSSRGBA *sourcepos;

for (y = 0; y < height; y++)
{
for (x = 0; x < width; x++)
{
nx = x - cx;
ny = y - cy;

transx = (float)(matrix[0] * nx + matrix[1] * ny + matrix[2]);
transy = (float)(matrix[3] * nx + matrix[4] * ny + matrix[5]);
transw = (float)(matrix[6] * nx + matrix[7] * ny + matrix[8]);

// Dividing by zero is bad
if (transw == 0)
transw = (float)0.001;

transx = (transx + cx) / transw;
transy = (transy + cy) / transw;

if (transx < 0 || transx > width || transy < 0 || transy > height)
{
red = 0;
green = 0;
blue = 0;
alpha = 0;
}
else
{
sourcepos = rowcopy + (int)transx + ((int)transy * width);

red = sourcepos->red;
green = sourcepos->green;
blue = sourcepos->blue;
alpha = sourcepos->alpha;
}

row->red = red;
row->green = green;
row->blue = blue;
row->alpha = alpha;

row++;
}
}

delete[]rowcopy;

return true;
}


I dont have a clue how to debug this.  When the game crashes, it just crashes.  No debug, no line number, no specific error, just "Stopped Working".

Thoughts?
Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)

Blizzard

It would be best if you set up some debug code with fprintf() that writes stuff to a file so you can see step by step what's going on.
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

KK20

Just a head's up, the +0.0001 should really be a minus (I don't know why you changed it). When using the square graphic in my example, if you remove the addition entirely, it was drawing the wrong row of pixels at every 90, 180, or 270 degrees. Watch the edges of the square and you will see the error.
Spoiler: ShowHide


Since cx and cy essentially cut the graphic width/height in half, you would get 48 for the 96x96 square graphic. Assuming 90 degrees, sin would be 1 and cos would be 0. If our loop variables (x,y) are (0,0), we multiply -48 (subtracted by cx and cy) to sin and cos and get transx and transy to be 48 and -48 respectively. Add cx and cy back to them and you get the pixel coordinate (96, 0), which is just outside of the bitmap.

If we made cx and cy to be barely 48 (hence subtract by 0.0001), we can still ensure that, at the end, we get (95,0).

And I realized that it can't be out of bounds since the if-statement at the end ensures we are always in bounds. Definitely suggest the printing to a text file if you know the DLL is truly causing it.

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

Heretic86

June 28, 2016, 03:09:34 am #35 Last Edit: June 28, 2016, 03:14:58 am by Heretic86
Oops, I forgot to put that back in...

This is what I got when monkeying with attaching the debugger to game.exe

The thread 0xf84 has exited with code 0 (0x0).
Unhandled exception at 0x10003A70 in Game.exe: 0xC0000005: Access violation reading location 0x10003A70.

Exception thrown at 0x10003A70 in Game.exe: 0xC0000005: Access violation executing location 0x10003A70.

Unhandled exception at 0x10003A70 in Game.exe: 0xC0000005: Access violation reading location 0x10003A70.

Exception thrown at 0x10003A70 in Game.exe: 0xC0000005: Access violation executing location 0x10003A70.


Donut have a clue.  All greek.  Doesnt point at anything in either source code.

---

Would RPG Maker dll versions have anything to do with it?  Im running on the 102e.dll by the way...
Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)

Blizzard

Most likely accessing data outside of the allowed rect.
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

Heretic86

Since this is related to the C++ part of the dynamic lighting, I'll ask in this thread.

Although I have heard the terms "linear interpolation", "bilinear interpolation" and "nearest neighbor", as far as the code goes (which I have not seen), what is the difference between these methods?
Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)

KK20

So given the destination coordinate (x,y), you throw this into your rotation algorithm and receive the coordinate (x1,y1). What you do with this new coordinate now depends on what interpolation you want to use.

Nearest neighbor, as is present in the current DLL, takes the pixel in the source image that is at the specified coordinates you calculated. If the coordinate is a float, it is usually rounded to the nearest integer, but the DLL currently ignores anything after the decimal point (it's up to you if you want N.5 to return N or N+1).

Meanwhile, Bi-linear interpolation keeps the float in your coordinate. Rather than taking a pixel from your source directly, it uses a "system of averages" to generate an entirely new pixel color based on the surrounding pixels.

If you could imagine (0,0) being the top left pixel of your image, (0.5,0) would be the right half of (0,0) and the left half of (1,0). You take the colors of these two pixels, average them out, and assign this to your destination bitmap.

Linear interpolation doesn't make sense in this case since we're dealing with 2D graphics. It's more for mathematical graphs. But there are many kinds of n-linear interpolations out there that we just group them all up under the same name.

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

Blizzard

July 02, 2016, 04:00:33 am #39 Last Edit: July 02, 2016, 04:09:19 am by Blizzard
Yeah, I was gonna say that. In 2D it's actually called bi-linear interpolation, because you interpolate on the X and Y axises. But the idea is basically the same.

Let's say we have 2 pixels: green and white. Or better said, we have 2x1 image with the values (0, 255, 0) and (255, 255, 255). Now, if would stretch that to 4 pixels, depending on the algorithm used, you will get a different result. Nearest neighbor will give you (0, 255, 0), (0, 255, 0), (255, 255, 255), (255, 255, 255) while lerp will give you (0, 255, 0), (85, 255, 85), (170, 255, 170), (255, 255, 255). Since the two pixels in between don't exist in the initial image, their values need to be calculated in a certain way. Nearest neighbor just takes a copy of the "nearest pixel" while lerp calculates the values as an average between the "extremes" (which are the original 2 pixels), depending on their distance from these extremes. So the second pixel will be a slightly brighter shade of green and the third a much brighter shade of green.

There are a few minor problems with bi-linear interpolation since you have to decide how to treat the image. Applying lerp to an image where you consider the "center of the pixel" as the reference point will yield difference results than if you consider the "top-left corner of the pixel" as reference point. And this also makes a lot of sense in terms of pixel data when working with floats. If you take the first pixel, its index is 0. The second one is 1. But logically you can still go to a coordinate like 1.99999999 which is virtually the top-right corner of the second pixel. I took this into account when writing my algorithm in XPA_Window which is why I keep suggesting you use it. xD I had to adjust the values and parameters for hours until I finally got it right. The trickiest part in this was to make sure that both upscaling and downscaling work properly since there seem to be logical differences in how you're supposed to treat the pixels. But once I actually got the algorithm right, the problem vanished by itself which also kinda confirmed that I finally did it right. xD
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

Heretic86

Thank you for the better explanations on Interpolation.

Im now looking at the source for XPA_Window and sort of see what is going on.  Looks like pixel per pixel, if there is a neighbor pixel to the left and / or right (with else ifs), it gets the average for each color value?
Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)

Blizzard

Yes, pretty much. I used several block since there is no need to do bi-linear if one of the factors/ratios (X and Y) is 0 and no lerp at all if both are 0. This speeds things up if you're e.g. stretching only horizontally or if you're stretching by an integer factor.
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

Heretic86

I havent started trying to incorporate your code yet.

I had one person give me a reply however:

Quote from: coyotecraftCan't play the demo. Missing VCRUNTIME140D.dll


Is this because he doesnt have VC Runtime installed, or possibly because the build I did was in Debug not Release?
Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)

Blizzard

Just take a look at my Visual Studio project properties in XPA_Window. I set stuff up so that no CRT is necessary. And make sure you compile as "Release", not "Debug".
Check out Daygames and our games:

King of Booze 2      King of Booze: Never Ever
Drinking Game for Android      Never have I ever for Android
Drinking Game for iOS      Never have I ever for iOS


Quote from: winkioI do not speak to bricks, either as individuals or in wall form.

Quote from: Barney StinsonWhen I get sad, I stop being sad and be awesome instead. True story.

Heretic86

Learning moar and moar every day.  I think I have this set correctly now...  Using release build without CRT stuff...
Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)

Heretic86

I think I've developed a bit of a Memory Leak...

Im not sure if it is coming from either the DLL, which I doubt, or my Ruby code, which seems more likely.  If I let the game run a while on a map, giving it about an hour or two, the game freezes up.  Only seems to occur when I rotate Bitmaps, I think.  Pain in the ass to find because it takes so damn long.

I have to end up using a lot of Bitmap.new calls, but I avoid at all costs.  I believe every New Temporary Bitmap is disposed of.  Does that help?  What else should I be doing after doing New Bitmaps to make sure I dont end up with a memory leak?

Bitmap.new(args)
bmp.dispose if bmp.is_a?(Bitmap) and not bmp.disposed?

The other big pain is that Im trying to cache as much as possible so I dont have to do Bitmap New as much, or use Clones.  Is there anything else that might be causing a Memory Leak, either in Ruby or C++?
Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)

KK20

What kind of freeze are we talking here? Was there an error message? Did FPS drop to nothing? Do you have your Task Manager open to Performance and seeing how much memory your game is using?

I also don't get what you're saying. You first say you're avoiding to make a lot of Bitmap.new calls and disposing the bitmap, but then you say you're caching as much as possible. So...what ARE you disposing?

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

Heretic86

Some framerate drops, but no error message, just that "... has stopped working" and normally, I'd only be able to close the program, but with Visual C++ installed, I do get the option to debug.  Problem is that is for RPG Maker, not the DLL, and we dont exactly have source code for RPG Maker.  Im mostly just wondering if not disposing of Bitmap New when assigned to a Local Variable in Ruby will be properly disposed of, even between methods...  Or at least best practices.  Im trying everything I can think of, Graphics.frame_reset, GC.start, etc.  Garbage Collection seems to help a lot with framerates, but still getting crashes.
Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)

KK20

Can you give a demo with the steps to reproduce?

Other Projects
RPG Maker XP Ace  Upgrade RMXP to RMVXA performance!
XPA Tilemap  Tilemap rewrite with many features, including custom resolution!

Nintendo Switch Friend Code: 8310-1917-5318
Discord: KK20 Tyler#8901

Join the CP Discord Server!

Heretic86

Current Scripts:
Heretic's Moving Platforms

Current Demos:
Collection of Art and 100% Compatible Scripts

(Script Demos are all still available in the Collection link above.  I lost some individual demos due to a server crash.)