Merge ssh://villavu.com:54367/simba
@ -1,3 +1,5 @@
|
|||||||
|
.. _mmlref-ocr:
|
||||||
|
|
||||||
TMOCR Class
|
TMOCR Class
|
||||||
===========
|
===========
|
||||||
|
|
||||||
@ -6,6 +8,177 @@ useful functions that can be used to create and identify text. It also contains
|
|||||||
some functions used in special cases to filter noise. Specifically, these are
|
some functions used in special cases to filter noise. Specifically, these are
|
||||||
all the ``Filter*`` functions.
|
all the ``Filter*`` functions.
|
||||||
|
|
||||||
How the filtering works internally is extensively documented in the code itself
|
.. _uptext-filter:
|
||||||
and therefore won't be documented here.
|
|
||||||
|
Uptext
|
||||||
|
------
|
||||||
|
|
||||||
|
To read the UpText, the TMOCR class applies several filters on the client data
|
||||||
|
before performing the actual OCR. We will take a look at the two filters first.
|
||||||
|
|
||||||
|
Filter 1: The Colour Filter
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
We first filter the raw client image with a very rough and tolerant colour
|
||||||
|
comparison / check.
|
||||||
|
We first convert the colour to RGB, and if it falls into the following
|
||||||
|
defined ranges, it may be part of the uptext. We also get the possible
|
||||||
|
shadows.
|
||||||
|
|
||||||
|
|
||||||
|
We will iterate over each pixel in the bitmap, and if it matches any of the
|
||||||
|
*rules* for the colour; we will set it to a constant colour which
|
||||||
|
represents this colour (and corresponding rule). Usually the *base*
|
||||||
|
colour. If it doesn't match any of the rules, it will be painted black.
|
||||||
|
We won't just check for colours, but also for differences between specific
|
||||||
|
R, G, B values. For example, if the colour is white; R, G and B should all
|
||||||
|
lie very close to each other. (That's what makes a colour white.)
|
||||||
|
|
||||||
|
The tolerance for getting the pixels is quite large. The reasons for the
|
||||||
|
high tolerance is because the uptext colour vary quite a lot. They're also
|
||||||
|
transparent and vary thus per background.
|
||||||
|
We will store/match shadow as well; we need it later on in filter 2.
|
||||||
|
|
||||||
|
To my knowledge this algorithm doesn't remove any *valid* points. It does
|
||||||
|
not remove *all* invalid points either; but that is simply not possible
|
||||||
|
based purely on the colour. (If someone has a good idea, let me know)
|
||||||
|
|
||||||
|
In code:
|
||||||
|
|
||||||
|
.. code-block:: pascal
|
||||||
|
|
||||||
|
for y := 0 to bmp.Height - 1 do
|
||||||
|
for x := 0 to bmp.Width - 1 do
|
||||||
|
begin
|
||||||
|
colortorgb(bmp.fastgetpixel(x,y),r,g,b);
|
||||||
|
|
||||||
|
if (r < ocr_Limit_Low) and (g < ocr_Limit_Low) and
|
||||||
|
(b < ocr_Limit_Low) then
|
||||||
|
begin
|
||||||
|
bmp.FastSetPixel(x,y, ocr_Purple);
|
||||||
|
continue;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Black if no match
|
||||||
|
bmp.fastsetpixel(x,y,0);
|
||||||
|
end;
|
||||||
|
|
||||||
|
Filter 2: The Characteristics Filter
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
This second filter is easy to understand but also very powerful:
|
||||||
|
|
||||||
|
- It removes *all* false shadow pixels.
|
||||||
|
- It removes uptext pixels that can't be uptext according to specific
|
||||||
|
rules. These rules are specifically designed so that it will never
|
||||||
|
throw away proper points.
|
||||||
|
|
||||||
|
It also performs another filter right at the start, but we'll disregard that
|
||||||
|
filter for now.
|
||||||
|
|
||||||
|
Removing shadow points is trivial if one understands the following insight.
|
||||||
|
|
||||||
|
If there some pixel is shadow on *x, y*, then it's neighbour *x+1, y+1*
|
||||||
|
may not be a shadow pixel. A shadow is always only one pixel *thick*.
|
||||||
|
|
||||||
|
With this in mind, we can easily define an algorithm which removes all false
|
||||||
|
shadow pixels. In code:
|
||||||
|
|
||||||
|
.. code-block:: pascal
|
||||||
|
|
||||||
|
{
|
||||||
|
The tricky part of the algorithm is that it starts at the bottom,
|
||||||
|
removing shadow point x,y if x-1,y-1 is also shadow. This is
|
||||||
|
more efficient than the obvious way. (It is also easier to implement)
|
||||||
|
}
|
||||||
|
|
||||||
|
for y := bmp.Height - 1 downto 1 do
|
||||||
|
for x := bmp.Width - 1 downto 1 do
|
||||||
|
begin
|
||||||
|
// Is it shadow?
|
||||||
|
if bmp.fastgetpixel(x,y) <> clPurple then
|
||||||
|
continue;
|
||||||
|
// Is the point at x-1,y-1 shadow? If it is
|
||||||
|
// then x, y cannot be shadow.
|
||||||
|
if bmp.fastgetpixel(x,y) = bmp.fastgetpixel(x-1,y-1) then
|
||||||
|
begin
|
||||||
|
bmp.fastsetpixel(x,y,clSilver);
|
||||||
|
continue;
|
||||||
|
end;
|
||||||
|
if bmp.fastgetpixel(x-1,y-1) = 0 then
|
||||||
|
bmp.fastsetpixel(x,y,clSilver);
|
||||||
|
end;
|
||||||
|
|
||||||
|
We are now left with only proper shadow pixels.
|
||||||
|
Now it is time to filter out false Uptext pixels.
|
||||||
|
|
||||||
|
Realize:
|
||||||
|
|
||||||
|
- If *x, y* is uptext, then *x+1, y+1* must be either uptext or shadow.
|
||||||
|
|
||||||
|
In code:
|
||||||
|
|
||||||
|
.. code-block:: pascal
|
||||||
|
|
||||||
|
for y := bmp.Height - 2 downto 0 do
|
||||||
|
for x := bmp.Width - 2 downto 0 do
|
||||||
|
begin
|
||||||
|
if bmp.fastgetpixel(x,y) = clPurple then
|
||||||
|
continue;
|
||||||
|
if bmp.fastgetpixel(x,y) = clBlack then
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Is the other pixel also uptext?
|
||||||
|
// NOTE THAT IT ALSO HAS TO BE THE SAME COLOUR
|
||||||
|
// UPTEXT IN THIS CASE.
|
||||||
|
// I'm still not sure if this is a good idea or not.
|
||||||
|
// Perhaps it should match *any* uptext colour.
|
||||||
|
if (bmp.fastgetpixel(x,y) = bmp.fastgetpixel(x+1,y+1) ) then
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// If it isn't shadow (and not the same colour uptext, see above)
|
||||||
|
// then it is not uptext.
|
||||||
|
if bmp.fastgetpixel(x+1,y+1) <> clPurple then
|
||||||
|
begin
|
||||||
|
bmp.fastsetpixel(x,y,clOlive);
|
||||||
|
continue;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// If we make it to here, it means the pixel is part of the uptext.
|
||||||
|
end;
|
||||||
|
|
||||||
|
Identifying characters
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
This part of the documentation is a bit vague and incomplete.
|
||||||
|
|
||||||
|
To actually identify the text we split it up into single character and then
|
||||||
|
pass each character to the OCR engine.
|
||||||
|
|
||||||
|
In the function *getTextPointsIn* we will use both the filters mentioned above.
|
||||||
|
After these have been applied, we will make a bitmap that only contains the
|
||||||
|
shadows as well as a bitmap that only contains the uptext chars (not the
|
||||||
|
shadows)
|
||||||
|
|
||||||
|
Now it is a good idea to count the occurances of all colours
|
||||||
|
(on the character bitmap); we will also use this later on.
|
||||||
|
To split the characters we use the well known *splittpaex* function.
|
||||||
|
|
||||||
|
We will then sort the points for in each character TPA, as this makes
|
||||||
|
makes looping over them and comparing distances easier. We will also
|
||||||
|
calculate the bounding box of each characters TPA.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Some more hackery is then used to seperate the characters and find
|
||||||
|
spaces; but isn't yet documented here.
|
||||||
|
|
||||||
|
Normal OCR
|
||||||
|
----------
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
To do :-)
|
||||||
|
A large part is already explained above.
|
||||||
|
Most of the other OCR functions are simply used for plain identifying
|
||||||
|
and have no filtering tasks.
|
||||||
|
|
||||||
|
@ -21,3 +21,4 @@ default (Pascal) engine.
|
|||||||
scriptref/bitmaps.rst
|
scriptref/bitmaps.rst
|
||||||
scriptref/string.rst
|
scriptref/string.rst
|
||||||
scriptref/tpa.rst
|
scriptref/tpa.rst
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
.. _scriptref_bitmaps:
|
.. _scriptref-bitmaps:
|
||||||
|
|
||||||
Bitmaps
|
Bitmaps
|
||||||
=======
|
=======
|
||||||
|
@ -83,7 +83,7 @@ GetColors
|
|||||||
|
|
||||||
function GetColors(const Coords : TPointArray) : TIntegerArray;
|
function GetColors(const Coords : TPointArray) : TIntegerArray;
|
||||||
|
|
||||||
GetColor returns the color on the coordinate (x, y) defined by *Coords*.
|
GetColors returns an array of the colours at the given *Coords*.
|
||||||
|
|
||||||
|
|
||||||
CountColor
|
CountColor
|
||||||
|
@ -80,7 +80,7 @@ BitmapFromText
|
|||||||
|
|
||||||
This function creates a bitmap from a string *text* with the given *font*.
|
This function creates a bitmap from a string *text* with the given *font*.
|
||||||
For an explanation on how to use and work with Bitmaps, please refer to
|
For an explanation on how to use and work with Bitmaps, please refer to
|
||||||
:ref:`scriptref_bitmaps`.
|
:ref:`scriptref-bitmaps`.
|
||||||
|
|
||||||
TPAFromText
|
TPAFromText
|
||||||
~~~~~~~~~~~
|
~~~~~~~~~~~
|
||||||
@ -91,7 +91,7 @@ TPAFromText
|
|||||||
|
|
||||||
This function creates a TPA from a string *text* with the given *font*.
|
This function creates a TPA from a string *text* with the given *font*.
|
||||||
For an explanation on how to use and work with TPAs, please refer to
|
For an explanation on how to use and work with TPAs, please refer to
|
||||||
:ref:`scriptref_tpointarray`.
|
:ref:`scriptref-tpointarray`.
|
||||||
|
|
||||||
TPAFromTextWrap
|
TPAFromTextWrap
|
||||||
~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~
|
||||||
@ -112,7 +112,7 @@ MaskFromText
|
|||||||
|
|
||||||
This function creates a Mask from a string *text* with the given *font*.
|
This function creates a Mask from a string *text* with the given *font*.
|
||||||
For an explanation on how to use and work with TPAs, please refer to
|
For an explanation on how to use and work with TPAs, please refer to
|
||||||
:ref:`scriptref_masks`.
|
:ref:`scriptref-masks`.
|
||||||
|
|
||||||
Reading Text
|
Reading Text
|
||||||
------------
|
------------
|
||||||
@ -127,6 +127,8 @@ rs_GetUpText
|
|||||||
This function is a function specific to RuneScape(tm); it reads the text
|
This function is a function specific to RuneScape(tm); it reads the text
|
||||||
in the upper left corner into a string.
|
in the upper left corner into a string.
|
||||||
|
|
||||||
|
How these functions actually work can be found here: :ref:`uptext-filter`.
|
||||||
|
|
||||||
rs_GetUpTextAt
|
rs_GetUpTextAt
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -12,6 +12,25 @@ Freeze
|
|||||||
function Freeze: boolean;
|
function Freeze: boolean;
|
||||||
|
|
||||||
|
|
||||||
|
If you call Freeze, the data that is *currently* currently in the client
|
||||||
|
is stored into memory. Simba will then target this memory for all further
|
||||||
|
finding operations; until *Unfreeze* is called. This can dramatically increase
|
||||||
|
speed if you don't care if the image doesn't change. It can be even more
|
||||||
|
important if you don't *want* the image to change; if you want to analyze a
|
||||||
|
specific frame.
|
||||||
|
|
||||||
|
Use like:
|
||||||
|
|
||||||
|
.. code-block:: pascal
|
||||||
|
Freeze;
|
||||||
|
|
||||||
|
if findcolors(...) then
|
||||||
|
...
|
||||||
|
|
||||||
|
Unfreeze
|
||||||
|
|
||||||
|
Make sure you never forget to call Unfreeze!
|
||||||
|
|
||||||
Unfreeze
|
Unfreeze
|
||||||
--------
|
--------
|
||||||
|
|
||||||
@ -19,6 +38,8 @@ Unfreeze
|
|||||||
|
|
||||||
function Unfreeze: boolean;
|
function Unfreeze: boolean;
|
||||||
|
|
||||||
|
Unfreeze the client data and restore the original client. See *Freeze* for more
|
||||||
|
details.
|
||||||
|
|
||||||
GetClientDimensions
|
GetClientDimensions
|
||||||
-------------------
|
-------------------
|
||||||
@ -27,6 +48,8 @@ GetClientDimensions
|
|||||||
|
|
||||||
procedure GetClientDimensions(var w, h:integer);
|
procedure GetClientDimensions(var w, h:integer);
|
||||||
|
|
||||||
|
Return the size of the client in *w* and *h*.
|
||||||
|
|
||||||
|
|
||||||
SetTargetBitmap
|
SetTargetBitmap
|
||||||
---------------
|
---------------
|
||||||
@ -35,6 +58,8 @@ SetTargetBitmap
|
|||||||
|
|
||||||
function SetTargetBitmap(Bitmap : integer): integer;
|
function SetTargetBitmap(Bitmap : integer): integer;
|
||||||
|
|
||||||
|
Set a bitmap as target / client. (It must be loaded by Simba)
|
||||||
|
|
||||||
|
|
||||||
SetTargetArray
|
SetTargetArray
|
||||||
--------------
|
--------------
|
||||||
@ -43,6 +68,10 @@ SetTargetArray
|
|||||||
|
|
||||||
function SetTargetArray(P: Integer; w, h: integer): integer;
|
function SetTargetArray(P: Integer; w, h: integer): integer;
|
||||||
|
|
||||||
|
Set a target array as client data. This is generally not something you'd
|
||||||
|
want to call yourself. It is mainly included for external components to allow
|
||||||
|
Simba to efficiently target its memory. See the SMART source on how to do this.
|
||||||
|
|
||||||
|
|
||||||
SetEIOSTarget
|
SetEIOSTarget
|
||||||
-------------
|
-------------
|
||||||
@ -115,6 +144,7 @@ SetDesktopAsClient
|
|||||||
|
|
||||||
procedure SetDesktopAsClient;
|
procedure SetDesktopAsClient;
|
||||||
|
|
||||||
|
Set the default desktop as client.
|
||||||
|
|
||||||
ActivateClient
|
ActivateClient
|
||||||
--------------
|
--------------
|
||||||
@ -123,6 +153,8 @@ ActivateClient
|
|||||||
|
|
||||||
procedure ActivateClient;
|
procedure ActivateClient;
|
||||||
|
|
||||||
|
Set the current target as active for key input.
|
||||||
|
|
||||||
|
|
||||||
IsTargetValid
|
IsTargetValid
|
||||||
-------------
|
-------------
|
||||||
@ -131,4 +163,5 @@ IsTargetValid
|
|||||||
|
|
||||||
function IsTargetValid: boolean;
|
function IsTargetValid: boolean;
|
||||||
|
|
||||||
|
Returns true if the current target is valid.
|
||||||
|
|
||||||
|
299
Extensions/paster.sex
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
program Paster;
|
||||||
|
//{$DEFINE DEV}
|
||||||
|
{$IFDEF EXTENSION}
|
||||||
|
var
|
||||||
|
Paster_Menu, GetPaste_MenuItem, Private_MenuItem: TMenuItem;
|
||||||
|
Browser_MenuItem, Paster_MenuItem, AltHost_Menu: TMenuItem;
|
||||||
|
Divider_MenuItems: array[1..2] of TMenuItem;
|
||||||
|
AltHost_Menus: array[1..5] of TMenuItem;
|
||||||
|
AltHost_MenuItems: array[1..5] of array[1..4] of TMenuItem;
|
||||||
|
|
||||||
|
function EncodeString(Data: string): string;
|
||||||
|
var
|
||||||
|
Pattern, Replacement: TStringArray;
|
||||||
|
I: integer;
|
||||||
|
begin
|
||||||
|
Pattern := ['\', #8, #9, #10, #11, #12, #13, '"', {#39,} '/'];
|
||||||
|
Replacement := ['\\', '\b', '\t', '\n', '\v', '\f', '\r', '\"', {'\'#39,} '\/'];
|
||||||
|
Result := Data;
|
||||||
|
if (Length(Pattern) = Length(Replacement)) then
|
||||||
|
for I := Low(Pattern) to High(Pattern) do
|
||||||
|
Result := Replace(Result, Pattern[I], Replacement[I], [rfIgnoreCase, rfReplaceAll]);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function GetName: string;
|
||||||
|
begin;
|
||||||
|
Result := 'Paster';
|
||||||
|
end;
|
||||||
|
|
||||||
|
function GetVersion: string;
|
||||||
|
begin;
|
||||||
|
Result := '0.2a';
|
||||||
|
end;
|
||||||
|
|
||||||
|
function JSONRequest(var Data: string; const HOST, Method: string): boolean;
|
||||||
|
begin
|
||||||
|
{$IFDEF DEV}
|
||||||
|
WriteLn('(HOST, Method) := ('#39 + HOST + #39', '#39 + Method + #39');');
|
||||||
|
WriteLn('Data := '#39 + Data + #39';');
|
||||||
|
{$ENDIF}
|
||||||
|
Data := GetPageEx('http://' + HOST + '/json/?method=' + Method, Data, 'application/json');
|
||||||
|
{$IFDEF DEV}WriteLn('Data := '#39 + Data + #39';');{$ENDIF}
|
||||||
|
if (Data = '') then
|
||||||
|
begin
|
||||||
|
Data := '{"data": null, "error": "Server unresponsive!"}';
|
||||||
|
Result := False;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if (Method = 'pastes.getPaste') then
|
||||||
|
begin
|
||||||
|
//Little hack for getPaste too work....
|
||||||
|
Data := Replace(Data, '{"data": {', '{', []);
|
||||||
|
Data := Replace(Data, '}, "error": ', ', "error": ', []);
|
||||||
|
end;
|
||||||
|
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function GetPaste(HOST: string): boolean;
|
||||||
|
var
|
||||||
|
Data: string;
|
||||||
|
begin
|
||||||
|
if (HOST = '') then
|
||||||
|
HOST := 'paste.sheeva.villavu.com';
|
||||||
|
if (InputQuery(GetName + ' ' + GetVersion + ' Extension', 'Which ID would you like too grab?', Data)) then
|
||||||
|
begin
|
||||||
|
if IntToStr(StrToInt(Data)) = Data then
|
||||||
|
Data := '{"paste_id": ' + Data + '}'
|
||||||
|
else
|
||||||
|
Data := '{"paste_id": "' + Data + '"}';
|
||||||
|
|
||||||
|
if (not (JSONRequest(Data, HOST, 'pastes.getPaste'))) then
|
||||||
|
begin
|
||||||
|
WriteLn('[Paster]Error: ' + GetJSONValue(Data, 'error'));
|
||||||
|
Result := False;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if ((GetJSONValue(Data, 'data') = 'null') and (GetJSONValue(Data, 'error') = 'null')) then
|
||||||
|
begin
|
||||||
|
WriteLn('[Paster]Error: Invalid Paste ID!');
|
||||||
|
Result := False;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
WriteLn('Opening Paste #' + GetJSONValue(Data, 'paste_id') + ' in a new tab!');
|
||||||
|
OpenScript('Paste #' + GetJSONValue(Data, 'paste_id'), GetJSONValue(Data, 'code'));
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function PasteIt(out Data: string; HOST: string): boolean;
|
||||||
|
begin
|
||||||
|
if (HOST = '') then
|
||||||
|
HOST := 'paste.sheeva.villavu.com';
|
||||||
|
if (MessageDlg(GetName + ' ' + GetVersion + ' Extension', 'Upload this script to ' + HOST + '?', mtConfirmation, [mbYes, mbNo], 0) = mrYes) then
|
||||||
|
begin
|
||||||
|
Data := '{"language": "delphi", "code": "' + EncodeString(ScriptText) + '", "private": ' + Lowercase(BoolToStr(Private_MenuItem.Checked)) + '}';
|
||||||
|
JSONRequest(Data, HOST, 'pastes.newPaste');
|
||||||
|
if (GetJSONValue(Data, 'error') = 'null') then
|
||||||
|
begin
|
||||||
|
Data := GetJSONValue(Data, 'data');
|
||||||
|
Result := True;
|
||||||
|
end else
|
||||||
|
Data := '[Paster]Error: ' + GetJSONValue(Data, 'error');
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure Paste(Host: string);
|
||||||
|
var
|
||||||
|
Data: string;
|
||||||
|
begin
|
||||||
|
if PasteIt(Data, Host) then
|
||||||
|
begin
|
||||||
|
if (Browser_MenuItem.Checked) then
|
||||||
|
begin
|
||||||
|
WriteLn('Opening pasted script at "http://' + HOST + '/show/' + Data + '/"!');
|
||||||
|
OpenWebPage('http://' + HOST + '/show/' + Data + '/');
|
||||||
|
end else
|
||||||
|
WriteLn('Script pasted at id "' + Data + '"');
|
||||||
|
end else
|
||||||
|
WriteLn(Data);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure UpdateHost(I: integer);
|
||||||
|
var
|
||||||
|
Data: string;
|
||||||
|
begin
|
||||||
|
if InputQuery(GetName + ' ' + GetVersion + ' Extension', 'Please input the LodgeIt Host! (Ex: paste.pocoo.org)', Data) then
|
||||||
|
begin
|
||||||
|
{$IFDEF DEV}WriteLn('Data := '#39 + Data + #39';');{$ENDIF}
|
||||||
|
if (Data = '') then
|
||||||
|
if (AltHost_Menus[I].Caption = 'Host ' + IntToStr(I)) then
|
||||||
|
begin
|
||||||
|
WriteLn('[Paster]Error: The host cannot be blank!');
|
||||||
|
Exit;
|
||||||
|
end else
|
||||||
|
begin
|
||||||
|
AltHost_Menus[I].Caption := 'Host ' + IntToStr(I);
|
||||||
|
AltHost_MenuItems[I][1].Enabled := False;
|
||||||
|
AltHost_MenuItems[I][2].Enabled := False;
|
||||||
|
Settings.setKeyValue('Host' + IntToStr(I), 'Host ' + IntToStr(I));
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
AltHost_Menus[I].Caption := Data;
|
||||||
|
AltHost_MenuItems[I][1].Enabled := True;
|
||||||
|
AltHost_MenuItems[I][2].Enabled := True;
|
||||||
|
Settings.setKeyValue('Host' + IntToStr(I), Data);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure OnClick(Sender: TObject);
|
||||||
|
var
|
||||||
|
I, K: integer;
|
||||||
|
begin;
|
||||||
|
case Sender of
|
||||||
|
Paster_MenuItem: Paste('');
|
||||||
|
GetPaste_MenuItem: GetPaste('');
|
||||||
|
Private_MenuItem: begin
|
||||||
|
Private_MenuItem.Checked := (not (Private_MenuItem.Checked));
|
||||||
|
Settings.setKeyValue('Private', Lowercase(BoolToStr(Private_MenuItem.Checked)));
|
||||||
|
{$IFDEF DEV}WriteLn('Private = ' + Lowercase(BoolToStr(Private_MenuItem.Checked)));{$ENDIF}
|
||||||
|
end;
|
||||||
|
Browser_MenuItem: begin
|
||||||
|
Browser_MenuItem.Checked := (not (Browser_MenuItem.Checked));
|
||||||
|
Settings.setKeyValue('OpenBrowser', Lowercase(BoolToStr(Browser_MenuItem.Checked)));
|
||||||
|
{$IFDEF DEV}WriteLn('OpenBrowser = ' + Lowercase(BoolToStr(Browser_MenuItem.Checked)));{$ENDIF}
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
for I := 1 to 5 do
|
||||||
|
for K := 1 to 4 do
|
||||||
|
if (Sender = AltHost_MenuItems[I][K]) then
|
||||||
|
begin
|
||||||
|
{$IFDEF DEV}WriteLn('Sender = ' + IntToStr(I) + ', ' + IntToStr(K));{$ENDIF}
|
||||||
|
if (K = 4) then
|
||||||
|
UpdateHost(I);
|
||||||
|
if (K = 2) then
|
||||||
|
GetPaste(Settings.getKeyValue('Host' + IntToStr(I)));
|
||||||
|
if (K = 1) then
|
||||||
|
Paste(Settings.getKeyValue('Host' + IntToStr(I)));
|
||||||
|
Break;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure Attach;
|
||||||
|
begin;
|
||||||
|
WriteLn(GetName + ' ' + GetVersion + ' Plugin Loaded!');
|
||||||
|
Paster_Menu.Visible := True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure Detach;
|
||||||
|
begin
|
||||||
|
Paster_Menu.Visible := False;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure Free;
|
||||||
|
var
|
||||||
|
I, K: integer;
|
||||||
|
begin
|
||||||
|
for I := 5 downto 1 do
|
||||||
|
begin
|
||||||
|
for K := 4 downto 1 do
|
||||||
|
AltHost_MenuItems[I][K].Free;
|
||||||
|
AltHost_Menus[I].Free;
|
||||||
|
end;
|
||||||
|
AltHost_Menu.Free;
|
||||||
|
Private_MenuItem.Free;
|
||||||
|
for I := 2 downto 1 do
|
||||||
|
Divider_MenuItems[I].Free;
|
||||||
|
Paster_MenuItem.Free;
|
||||||
|
Paster_Menu.Free;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure init;
|
||||||
|
var
|
||||||
|
I, K: integer;
|
||||||
|
begin;
|
||||||
|
Paster_Menu := TMenuItem.Create(Simba_MainMenu);
|
||||||
|
Paster_Menu.Caption := GetName;
|
||||||
|
Simba_MainMenu.Items.Add(Paster_Menu);
|
||||||
|
|
||||||
|
Paster_MenuItem := TMenuItem.Create(Paster_Menu);
|
||||||
|
with Paster_MenuItem do
|
||||||
|
begin
|
||||||
|
Caption := 'Paste It!';
|
||||||
|
OnClick := @OnClick;
|
||||||
|
end;
|
||||||
|
Paster_Menu.Add(Paster_MenuItem);
|
||||||
|
|
||||||
|
GetPaste_MenuItem := TMenuItem.Create(Paster_Menu);
|
||||||
|
with GetPaste_MenuItem do
|
||||||
|
begin
|
||||||
|
Caption := 'Get Paste!';
|
||||||
|
OnClick := @OnClick;
|
||||||
|
end;
|
||||||
|
Paster_Menu.Add(GetPaste_MenuItem);
|
||||||
|
|
||||||
|
for I := 1 to 2 do
|
||||||
|
begin
|
||||||
|
Divider_MenuItems[I] := TMenuItem.Create(Paster_Menu);
|
||||||
|
Divider_MenuItems[I].Caption := '-';
|
||||||
|
end;
|
||||||
|
|
||||||
|
Paster_Menu.Add(Divider_MenuItems[1]);
|
||||||
|
|
||||||
|
Private_MenuItem := TMenuItem.Create(Paster_Menu);
|
||||||
|
with Private_MenuItem do
|
||||||
|
begin
|
||||||
|
Caption := 'Private';
|
||||||
|
OnClick := @OnClick;
|
||||||
|
Checked := (Lowercase(Settings.getKeyValueDef('Private', 'true')) = 'true');
|
||||||
|
end;
|
||||||
|
Paster_Menu.Add(Private_MenuItem);
|
||||||
|
|
||||||
|
Browser_MenuItem := TMenuItem.Create(Paster_Menu);
|
||||||
|
with Browser_MenuItem do
|
||||||
|
begin
|
||||||
|
Caption := 'Open in Browser';
|
||||||
|
OnClick := @OnClick;
|
||||||
|
Checked := (Lowercase(Settings.getKeyValueDef('OpenBrowser', 'true')) = 'true');
|
||||||
|
end;
|
||||||
|
Paster_Menu.Add(Browser_MenuItem);
|
||||||
|
|
||||||
|
Paster_Menu.Add(Divider_MenuItems[2]);
|
||||||
|
|
||||||
|
AltHost_Menu := TMenuItem.Create(Paster_Menu);
|
||||||
|
AltHost_Menu.Caption := 'Alternate Hosts';
|
||||||
|
Paster_Menu.Add(AltHost_Menu);
|
||||||
|
|
||||||
|
for I := 1 to 5 do
|
||||||
|
begin
|
||||||
|
AltHost_Menus[I] := TMenuItem.Create(AltHost_Menu);
|
||||||
|
AltHost_Menus[I].Caption := Settings.getKeyValueDef('Host' + IntToStr(I), 'Host ' + IntToStr(I));
|
||||||
|
AltHost_Menu.Add(AltHost_Menus[I]);
|
||||||
|
|
||||||
|
for K := 1 to 4 do
|
||||||
|
begin
|
||||||
|
AltHost_MenuItems[I][K] := TMenuItem.Create(AltHost_Menus[I]);
|
||||||
|
case K of
|
||||||
|
1: AltHost_MenuItems[I][1].Caption := 'Paste It!';
|
||||||
|
2: AltHost_MenuItems[I][2].Caption := 'Get Paste!';
|
||||||
|
3: AltHost_MenuItems[I][3].Caption := '-';
|
||||||
|
4: AltHost_MenuItems[I][4].Caption := 'Update Host';
|
||||||
|
end;
|
||||||
|
AltHost_MenuItems[I][K].OnClick := @OnClick;
|
||||||
|
if ((not ((K = 3) or (K = 4))) and (AltHost_Menus[I].Caption = 'Host ' + IntToStr(I))) then
|
||||||
|
AltHost_MenuItems[I][K].Enabled := False;
|
||||||
|
|
||||||
|
AltHost_Menus[I].Add(AltHost_MenuItems[I][K]);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
{$ENDIF}
|
||||||
|
begin
|
||||||
|
{$IFNDEF EXTENSION}
|
||||||
|
WriteLn('This is a Extension for Simba. If you are in Simba click View->Extensions and enable paster.sex!');
|
||||||
|
{$ENDIF}
|
||||||
|
end.
|
||||||
|
|
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 342 B After Width: | Height: | Size: 344 B |
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 270 B After Width: | Height: | Size: 272 B |
Before Width: | Height: | Size: 270 B After Width: | Height: | Size: 272 B |
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 270 B After Width: | Height: | Size: 272 B |
Before Width: | Height: | Size: 702 B After Width: | Height: | Size: 704 B |
Before Width: | Height: | Size: 558 B After Width: | Height: | Size: 560 B |
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 414 B After Width: | Height: | Size: 416 B |
Before Width: | Height: | Size: 342 B After Width: | Height: | Size: 344 B |
Before Width: | Height: | Size: 558 B After Width: | Height: | Size: 560 B |
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 630 B After Width: | Height: | Size: 632 B |
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 558 B After Width: | Height: | Size: 560 B |
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 270 B After Width: | Height: | Size: 272 B |
Before Width: | Height: | Size: 270 B After Width: | Height: | Size: 272 B |
Before Width: | Height: | Size: 270 B After Width: | Height: | Size: 272 B |
Before Width: | Height: | Size: 342 B After Width: | Height: | Size: 416 B |
Before Width: | Height: | Size: 270 B After Width: | Height: | Size: 272 B |
BIN
Fonts/CharsNPC/48.bmp
Normal file
After Width: | Height: | Size: 704 B |
BIN
Fonts/CharsNPC/49.bmp
Normal file
After Width: | Height: | Size: 488 B |
BIN
Fonts/CharsNPC/50.bmp
Normal file
After Width: | Height: | Size: 704 B |
BIN
Fonts/CharsNPC/51.bmp
Normal file
After Width: | Height: | Size: 704 B |
BIN
Fonts/CharsNPC/52.bmp
Normal file
After Width: | Height: | Size: 632 B |
BIN
Fonts/CharsNPC/53.bmp
Normal file
After Width: | Height: | Size: 704 B |
BIN
Fonts/CharsNPC/54.bmp
Normal file
After Width: | Height: | Size: 704 B |
BIN
Fonts/CharsNPC/55.bmp
Normal file
After Width: | Height: | Size: 632 B |
BIN
Fonts/CharsNPC/56.bmp
Normal file
After Width: | Height: | Size: 704 B |
BIN
Fonts/CharsNPC/57.bmp
Normal file
After Width: | Height: | Size: 704 B |
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 558 B After Width: | Height: | Size: 560 B |
Before Width: | Height: | Size: 702 B After Width: | Height: | Size: 704 B |
Before Width: | Height: | Size: 630 B After Width: | Height: | Size: 632 B |
Before Width: | Height: | Size: 774 B After Width: | Height: | Size: 776 B |
Before Width: | Height: | Size: 702 B After Width: | Height: | Size: 704 B |
Before Width: | Height: | Size: 630 B After Width: | Height: | Size: 632 B |
Before Width: | Height: | Size: 702 B After Width: | Height: | Size: 704 B |
Before Width: | Height: | Size: 702 B After Width: | Height: | Size: 704 B |
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 702 B After Width: | Height: | Size: 704 B |
Before Width: | Height: | Size: 558 B After Width: | Height: | Size: 560 B |
Before Width: | Height: | Size: 918 B After Width: | Height: | Size: 920 B |
Before Width: | Height: | Size: 846 B After Width: | Height: | Size: 848 B |
Before Width: | Height: | Size: 702 B After Width: | Height: | Size: 704 B |
Before Width: | Height: | Size: 702 B After Width: | Height: | Size: 704 B |
Before Width: | Height: | Size: 702 B After Width: | Height: | Size: 704 B |
Before Width: | Height: | Size: 702 B After Width: | Height: | Size: 704 B |
Before Width: | Height: | Size: 558 B After Width: | Height: | Size: 560 B |
Before Width: | Height: | Size: 702 B After Width: | Height: | Size: 704 B |
Before Width: | Height: | Size: 702 B After Width: | Height: | Size: 704 B |
Before Width: | Height: | Size: 702 B After Width: | Height: | Size: 704 B |
Before Width: | Height: | Size: 918 B After Width: | Height: | Size: 920 B |
Before Width: | Height: | Size: 920 B After Width: | Height: | Size: 920 B |
Before Width: | Height: | Size: 702 B After Width: | Height: | Size: 704 B |
Before Width: | Height: | Size: 630 B After Width: | Height: | Size: 632 B |
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 486 B After Width: | Height: | Size: 488 B |
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 464 B |
@ -6,4 +6,5 @@ Linux:
|
|||||||
* IsKeyDown doesn't work yet.
|
* IsKeyDown doesn't work yet.
|
||||||
|
|
||||||
Windows:
|
Windows:
|
||||||
|
* Interpreter doesn't work well on x64.
|
||||||
|
|
||||||
|
BIN
Plugins/libsmart.dll
Normal file → Executable file
@ -64,7 +64,7 @@ begin
|
|||||||
AboutMemo.Lines.Add('Simba is released under the GPL license.');
|
AboutMemo.Lines.Add('Simba is released under the GPL license.');
|
||||||
AboutMemo.Lines.Add(format('You are currently using version: %d',[SimbaUnit.SimbaVersion]));
|
AboutMemo.Lines.Add(format('You are currently using version: %d',[SimbaUnit.SimbaVersion]));
|
||||||
AboutMemo.Lines.Add('');
|
AboutMemo.Lines.Add('');
|
||||||
AboutMemo.Lines.Add('Please report bugs at: http://mufasa.villavu.com/mantis/');
|
AboutMemo.Lines.Add('Please report bugs at: http://bugs.villavu.com/');
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TAboutForm.OkButtonClick(Sender: TObject);
|
procedure TAboutForm.OkButtonClick(Sender: TObject);
|
||||||
|
@ -17,7 +17,7 @@ object ScriptFrame: TScriptFrame
|
|||||||
Font.Height = -13
|
Font.Height = -13
|
||||||
Font.Name = 'Courier New'
|
Font.Name = 'Courier New'
|
||||||
Font.Pitch = fpFixed
|
Font.Pitch = fpFixed
|
||||||
Font.Quality = fqNonAntialiased
|
Font.Quality = fqProof
|
||||||
ParentColor = False
|
ParentColor = False
|
||||||
ParentFont = False
|
ParentFont = False
|
||||||
PopupMenu = SimbaForm.ScriptPopup
|
PopupMenu = SimbaForm.ScriptPopup
|
||||||
@ -28,7 +28,7 @@ object ScriptFrame: TScriptFrame
|
|||||||
OnKeyPress = SynEditKeyPress
|
OnKeyPress = SynEditKeyPress
|
||||||
OnClickLink = SynEditClickLink
|
OnClickLink = SynEditClickLink
|
||||||
OnMouseLink = SynEditMouseLink
|
OnMouseLink = SynEditMouseLink
|
||||||
Gutter.Width = 57
|
Gutter.Width = 59
|
||||||
Gutter.MouseActions = <
|
Gutter.MouseActions = <
|
||||||
item
|
item
|
||||||
Shift = []
|
Shift = []
|
||||||
@ -627,7 +627,7 @@ object ScriptFrame: TScriptFrame
|
|||||||
Width = 24
|
Width = 24
|
||||||
end
|
end
|
||||||
object TSynGutterLineNumber
|
object TSynGutterLineNumber
|
||||||
Width = 17
|
Width = 19
|
||||||
MouseActions = <>
|
MouseActions = <>
|
||||||
MarkupInfo.Background = clBtnFace
|
MarkupInfo.Background = clBtnFace
|
||||||
MarkupInfo.Foreground = clNone
|
MarkupInfo.Foreground = clNone
|
||||||
|
@ -66,6 +66,8 @@ uses
|
|||||||
tpa, //Tpa stuff
|
tpa, //Tpa stuff
|
||||||
SynRegExpr,
|
SynRegExpr,
|
||||||
lclintf,
|
lclintf,
|
||||||
|
httpsend,
|
||||||
|
superobject,
|
||||||
SimbaUnit,updateform, mmisc, mmlpsthread; // for GetTickCount and others.//Writeln
|
SimbaUnit,updateform, mmisc, mmlpsthread; // for GetTickCount and others.//Writeln
|
||||||
|
|
||||||
{$ifdef Linux}
|
{$ifdef Linux}
|
||||||
@ -181,7 +183,11 @@ begin
|
|||||||
AddFunction(@ext_UnTarEx,'function UnTarEx(const Input : string;const outputdir : string; overwrite : boolean): boolean;');
|
AddFunction(@ext_UnTarEx,'function UnTarEx(const Input : string;const outputdir : string; overwrite : boolean): boolean;');
|
||||||
AddFunction(@ext_MessageDlg,'function MessageDlg(const aCaption, aMsg: string; DlgType: TMsgDlgType;Buttons: TMsgDlgButtons; HelpCtx: Longint): Integer;');
|
AddFunction(@ext_MessageDlg,'function MessageDlg(const aCaption, aMsg: string; DlgType: TMsgDlgType;Buttons: TMsgDlgButtons; HelpCtx: Longint): Integer;');
|
||||||
AddFunction(@ext_InputQuery,'function InputQuery(const ACaption, APrompt : String; var Value : String) : Boolean;');
|
AddFunction(@ext_InputQuery,'function InputQuery(const ACaption, APrompt : String; var Value : String) : Boolean;');
|
||||||
|
AddFunction(@ext_ScriptText,'function ScriptText: string;');
|
||||||
|
AddFunction(@ext_OpenScript,'procedure OpenScript(Name, Data: string);');
|
||||||
AddRegisteredPTRVariable('Settings','TMMLSettingsSandbox');
|
AddRegisteredPTRVariable('Settings','TMMLSettingsSandbox');
|
||||||
|
AddFunction(@ext_GetPageEx,'function GetPageEx(const URL, PostData, MimeType: string): string;');
|
||||||
|
AddFunction(@ext_GetJSONValue,'function GetJSONValue(const Data, Value: string): string;');
|
||||||
AddRegisteredVariable('Simba','TForm');
|
AddRegisteredVariable('Simba','TForm');
|
||||||
AddRegisteredVariable('Simba_MainMenu','TMainMenu');
|
AddRegisteredVariable('Simba_MainMenu','TMainMenu');
|
||||||
AddRegisteredVariable('Client','TClient');
|
AddRegisteredVariable('Client','TClient');
|
||||||
|
@ -45,7 +45,7 @@ uses
|
|||||||
CastaliaSimplePasPar, v_AutoCompleteForm, PSDump, settings, updater;
|
CastaliaSimplePasPar, v_AutoCompleteForm, PSDump, settings, updater;
|
||||||
|
|
||||||
const
|
const
|
||||||
SimbaVersion = 715;
|
SimbaVersion = 720;
|
||||||
|
|
||||||
interp_PS = 0; //PascalScript
|
interp_PS = 0; //PascalScript
|
||||||
interp_RT = 1; //RUTIS
|
interp_RT = 1; //RUTIS
|
||||||
|
@ -122,3 +122,53 @@ function ext_InputQuery(const ACaption, APrompt : String; var Value : String) :
|
|||||||
begin
|
begin
|
||||||
result := InputQuery(acaption,aprompt,value);
|
result := InputQuery(acaption,aprompt,value);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function ext_ScriptText: string;
|
||||||
|
begin
|
||||||
|
Result := SimbaForm.CurrScript.SynEdit.Lines.Text;
|
||||||
|
Result := ReplaceRegExpr('([N|n][A|a][M|m][E|e]|[P|p][A|a][S|s]{2}|[P|p][I|i][N|n])\s*\:\=\s*\''.*?\'';', Result, '$1 := ''*********'';', True);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure ext_OpenScript(Name, Data: string);
|
||||||
|
begin
|
||||||
|
if (Name = '') then
|
||||||
|
Name := 'Untitled';
|
||||||
|
SimbaForm.AddTab;
|
||||||
|
SimbaForm.CurrScript.SynEdit.Lines.Text := Data;
|
||||||
|
SimbaForm.CurrScript.ScriptName := Name;
|
||||||
|
SimbaForm.RefreshTab;
|
||||||
|
SimbaForm.UpdateTitle;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function ext_GetPageEx(const URL, PostData, MimeType: string): string;
|
||||||
|
var
|
||||||
|
HTTPSend: THTTPSend;
|
||||||
|
begin
|
||||||
|
HTTPSend := THTTPSend.Create;
|
||||||
|
try
|
||||||
|
HTTPSend.MimeType := MimeType;
|
||||||
|
HTTPSend.Document.Clear;
|
||||||
|
HTTPSend.Document.Write(PostData[1], Length(PostData));
|
||||||
|
try
|
||||||
|
if HTTPSend.HTTPMethod('POST', URL) then
|
||||||
|
begin;
|
||||||
|
SetLength(Result, HTTPSend.Document.Size);
|
||||||
|
HTTPSend.Document.Read(Result[1], Length(Result));
|
||||||
|
end else
|
||||||
|
Result := '';
|
||||||
|
except
|
||||||
|
on e : exception do
|
||||||
|
mDebugLn('Exception in GetPage in Extensions: ' + e.message);
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
HTTPSend.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function ext_GetJSONValue(const Data, Value: string): string;
|
||||||
|
var
|
||||||
|
SuperObject: ISuperObject;
|
||||||
|
begin
|
||||||
|
SuperObject := SO(Data);
|
||||||
|
Result := SuperObject.AsObject.S[Value];
|
||||||
|
end;
|
||||||
|
@ -3,6 +3,11 @@ begin
|
|||||||
Result := CurrThread.Client.MOCR.GetUpTextAtEx(7, 7, true);
|
Result := CurrThread.Client.MOCR.GetUpTextAtEx(7, 7, true);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function ps_rs_GetUpTextAtEx(x, y: integer; shadow: boolean): string; extdecl;
|
||||||
|
begin
|
||||||
|
result := CurrThread.Client.MOCR.GetUpTextAtEx(x, y, shadow);
|
||||||
|
end;
|
||||||
|
|
||||||
function ps_rs_GetUpTextAt(x, y : integer): string; extdecl;
|
function ps_rs_GetUpTextAt(x, y : integer): string; extdecl;
|
||||||
begin
|
begin
|
||||||
result := CurrThread.Client.MOCR.GetUpTextAtEx(x,y,true);
|
result := CurrThread.Client.MOCR.GetUpTextAtEx(x,y,true);
|
||||||
|
@ -316,6 +316,7 @@ AddFunction(@ps_GetKeyCode,'function GetKeyCode(c : char) : integer;');
|
|||||||
SetCurrSection('OCR');
|
SetCurrSection('OCR');
|
||||||
AddFunction(@ps_rs_GetUpText, 'function rs_GetUpText: string;');
|
AddFunction(@ps_rs_GetUpText, 'function rs_GetUpText: string;');
|
||||||
AddFunction(@ps_rs_GetUpTextAt, 'function rs_GetUpTextAt(x, y : integer): string;');
|
AddFunction(@ps_rs_GetUpTextAt, 'function rs_GetUpTextAt(x, y : integer): string;');
|
||||||
|
AddFunction(@ps_rs_GetUpTextAtEx, 'function rs_GetUpTextAtEx(x, y: integer; shadow: boolean): string');
|
||||||
AddFunction(@ps_BitmapFromText, 'function BitmapFromText(const text, font: String): integer;');
|
AddFunction(@ps_BitmapFromText, 'function BitmapFromText(const text, font: String): integer;');
|
||||||
AddFunction(@ps_TPAFromText, 'function TPAFromText(const text, font: String;var w,h : integer): TPointArray;');
|
AddFunction(@ps_TPAFromText, 'function TPAFromText(const text, font: String;var w,h : integer): TPointArray;');
|
||||||
AddFunction(@ps_TPAFromTextWrap,'procedure TPAFromTextWrap(const text, font: String;var w,h : integer;var TPA : TPointArray);');
|
AddFunction(@ps_TPAFromTextWrap,'procedure TPAFromTextWrap(const text, font: String;var w,h : integer;var TPA : TPointArray);');
|
||||||
|
@ -202,6 +202,7 @@ function TMFonts.LoadFont(const Name: String; Shadow: Boolean): boolean;
|
|||||||
var
|
var
|
||||||
f: TMFont;
|
f: TMFont;
|
||||||
begin
|
begin
|
||||||
|
Result := True;
|
||||||
if not DirectoryExists(FPath + Name) then
|
if not DirectoryExists(FPath + Name) then
|
||||||
begin
|
begin
|
||||||
raise Exception.Create('LoadFont: Directory ' + FPath + Name + ' does not exists.');
|
raise Exception.Create('LoadFont: Directory ' + FPath + Name + ' does not exists.');
|
||||||
|
@ -23,6 +23,7 @@ interface
|
|||||||
procedure FreePlugins;
|
procedure FreePlugins;
|
||||||
procedure LoadPluginsDir(DirIndex : integer);
|
procedure LoadPluginsDir(DirIndex : integer);
|
||||||
function VerifyPath(Path : string) : string;
|
function VerifyPath(Path : string) : string;
|
||||||
|
function LoadPluginNoFallback(PluginName : string) : integer;
|
||||||
protected
|
protected
|
||||||
function InitPlugin(plugin: TLibHandle): boolean; virtual; abstract;
|
function InitPlugin(plugin: TLibHandle): boolean; virtual; abstract;
|
||||||
public
|
public
|
||||||
@ -113,8 +114,7 @@ implementation
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TGenericLoader.LoadPluginNoFallback(PluginName: string): Integer;
|
||||||
function TGenericLoader.LoadPlugin(PluginName: string): Integer;
|
|
||||||
var
|
var
|
||||||
i, ii : integer;
|
i, ii : integer;
|
||||||
PlugExt: String = {$IFDEF LINUX}'.so';{$ELSE}'.dll';{$ENDIF}
|
PlugExt: String = {$IFDEF LINUX}'.so';{$ELSE}'.dll';{$ENDIF}
|
||||||
@ -151,6 +151,19 @@ implementation
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
function TGenericLoader.LoadPlugin(PluginName: string): Integer;
|
||||||
|
var
|
||||||
|
CpuBits: String = {$IFDEF CPU32}'32';{$ELSE}'64';{$ENDIF}
|
||||||
|
begin
|
||||||
|
try
|
||||||
|
Result := LoadPluginNoFallback(PluginName);
|
||||||
|
except
|
||||||
|
on exception do Result:= LoadPluginNoFallback(PluginName+CpuBits);
|
||||||
|
end;
|
||||||
|
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
constructor TGenericLoader.Create;
|
constructor TGenericLoader.Create;
|
||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
|
@ -52,7 +52,7 @@ type
|
|||||||
function GetUpTextAtEx(atX, atY: integer; shadow: boolean): string;
|
function GetUpTextAtEx(atX, atY: integer; shadow: boolean): string;
|
||||||
function GetUpTextAt(atX, atY: integer; shadow: boolean): string;
|
function GetUpTextAt(atX, atY: integer; shadow: boolean): string;
|
||||||
|
|
||||||
procedure FilterUpTextByColour(bmp: TMufasaBitmap; w,h: integer);
|
procedure FilterUpTextByColour(bmp: TMufasaBitmap);
|
||||||
procedure FilterUpTextByCharacteristics(bmp: TMufasaBitmap; w,h: integer);
|
procedure FilterUpTextByCharacteristics(bmp: TMufasaBitmap; w,h: integer);
|
||||||
procedure FilterShadowBitmap(bmp: TMufasaBitmap);
|
procedure FilterShadowBitmap(bmp: TMufasaBitmap);
|
||||||
procedure FilterCharsBitmap(bmp: TMufasaBitmap);
|
procedure FilterCharsBitmap(bmp: TMufasaBitmap);
|
||||||
@ -197,84 +197,124 @@ end;
|
|||||||
Non optimised. We can make it use direct data instead of fastgetpixel and
|
Non optimised. We can make it use direct data instead of fastgetpixel and
|
||||||
fastsetpixel, but speed isn't really an issue. The entire algorithm is still
|
fastsetpixel, but speed isn't really an issue. The entire algorithm is still
|
||||||
fast enough.
|
fast enough.
|
||||||
|
|
||||||
|
There are some issues with this method; since later on the algorithm
|
||||||
|
(if I recall correctly) throws aways some pixels if a character isn't just
|
||||||
|
one colour)
|
||||||
|
|
||||||
|
We will match shadow as well; we need it later on.
|
||||||
}
|
}
|
||||||
|
|
||||||
procedure TMOCR.FilterUpTextByColour(bmp: TMufasaBitmap; w,h: integer);
|
procedure TMOCR.FilterUpTextByColour(bmp: TMufasaBitmap);
|
||||||
var
|
var
|
||||||
x, y,r, g, b: Integer;
|
x, y,r, g, b: Integer;
|
||||||
begin
|
begin
|
||||||
// We're going to filter the bitmap solely on colours first.
|
{
|
||||||
// If we found one, we set it to it's `normal' colour.
|
We're going to filter the bitmap solely on colours first.
|
||||||
|
If we found one, we set it to it's `normal' colour.
|
||||||
|
|
||||||
|
Note that these values aren't randomly chosen, but I didn't spent too
|
||||||
|
much time on finding the exact values either.
|
||||||
|
|
||||||
|
We will iterate over each pixel in the bitmap, and if it matches any of the
|
||||||
|
``rules'' for the colour; we will set it to a constant colour which
|
||||||
|
represents this colour (and corresponding rule). Usually the ``base''
|
||||||
|
colour.
|
||||||
|
|
||||||
|
If it doesn't match any rule, we can safely make the pixel black.
|
||||||
|
|
||||||
|
To my knowledge this algorithm doesn't remove any valid points. It does
|
||||||
|
not remove *all* invalid points either; but that is simply not possible
|
||||||
|
based purely on the colour. (If someone has a good idea, let me know)
|
||||||
|
}
|
||||||
for y := 0 to bmp.Height - 1 do
|
for y := 0 to bmp.Height - 1 do
|
||||||
for x := 0 to bmp.Width - 1 do
|
for x := 0 to bmp.Width - 1 do
|
||||||
begin
|
begin
|
||||||
colortorgb(bmp.fastgetpixel(x,y),r,g,b);
|
colortorgb(bmp.fastgetpixel(x,y),r,g,b);
|
||||||
// the abs(g-b) < 15 seems to help heaps when taking out crap points
|
{
|
||||||
|
abs(g-b) < 15 seems to help heaps when taking out invalid (white) points
|
||||||
|
obviously if a colour is ``white'' R, G B should all be approx the
|
||||||
|
same value. That is what the last part of the if statement checks.
|
||||||
|
}
|
||||||
if (r > ocr_Limit_High) and (g > ocr_Limit_High) and (b > ocr_Limit_High)
|
if (r > ocr_Limit_High) and (g > ocr_Limit_High) and (b > ocr_Limit_High)
|
||||||
// 50 or 55. 55 seems to be better.
|
|
||||||
and (abs(r-g) + abs(r-b) + abs(g-b) < 55) then
|
and (abs(r-g) + abs(r-b) + abs(g-b) < 55) then
|
||||||
// TODO: make 55 a var, and make it so that it can be set
|
|
||||||
begin
|
begin
|
||||||
bmp.fastsetpixel(x,y,ocr_White);
|
bmp.fastsetpixel(x,y,ocr_White);
|
||||||
continue;
|
continue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Quite straightforward and works quite well, afaik }
|
||||||
if (r < ocr_Limit_Low) and (g > ocr_Limit_High) and (b > ocr_Limit_High) then
|
if (r < ocr_Limit_Low) and (g > ocr_Limit_High) and (b > ocr_Limit_High) then
|
||||||
begin
|
begin
|
||||||
bmp.fastsetpixel(x,y,ocr_Blue);
|
bmp.fastsetpixel(x,y,ocr_Blue);
|
||||||
continue;
|
continue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Ditto }
|
||||||
if (r < ocr_Limit_Low) and (g > ocr_Limit_High) and (b < ocr_Limit_Low) then
|
if (r < ocr_Limit_Low) and (g > ocr_Limit_High) and (b < ocr_Limit_Low) then
|
||||||
begin
|
begin
|
||||||
bmp.fastsetpixel(x,y,ocr_Green);
|
bmp.fastsetpixel(x,y,ocr_Green);
|
||||||
continue;
|
continue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// false results with fire
|
{ False results with fire }
|
||||||
if(r > ocr_Limit_High) and (g > 100) and (g < ocr_Limit_High) and (b > 40) and (b < 127) then
|
if(r > ocr_Limit_High) and (g > 100) and (g < ocr_Limit_High) and (b > 40) and (b < 127) then
|
||||||
begin
|
begin
|
||||||
bmp.fastsetpixel(x,y,ocr_ItemC);
|
bmp.fastsetpixel(x,y,ocr_ItemC);
|
||||||
continue;
|
continue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Works fine afaik }
|
||||||
if(r > ocr_Limit_High) and (g > ocr_Limit_High) and (b < ocr_Limit_Low) then
|
if(r > ocr_Limit_High) and (g > ocr_Limit_High) and (b < ocr_Limit_Low) then
|
||||||
begin
|
begin
|
||||||
bmp.fastsetpixel(x,y,ocr_Yellow);
|
bmp.fastsetpixel(x,y,ocr_Yellow);
|
||||||
continue;
|
continue;
|
||||||
end;
|
end;
|
||||||
// better use g < 40 than ocr_Limit_Low imo
|
|
||||||
|
// better use g < 40 than ocr_Limit_Low imo (TODO)
|
||||||
if (r > ocr_Limit_High) and (g < ocr_Limit_Low) and (b < ocr_Limit_Low) then
|
if (r > ocr_Limit_High) and (g < ocr_Limit_Low) and (b < ocr_Limit_Low) then
|
||||||
begin
|
begin
|
||||||
bmp.fastsetpixel(x,y,ocr_Red);
|
bmp.fastsetpixel(x,y,ocr_Red);
|
||||||
continue;
|
continue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Red as well }
|
||||||
if (r > ocr_Limit_High) and (g > ocr_Limit_Low) and (b < ocr_Limit_Low) then
|
if (r > ocr_Limit_High) and (g > ocr_Limit_Low) and (b < ocr_Limit_Low) then
|
||||||
begin
|
begin
|
||||||
bmp.fastsetpixel(x,y,ocr_Red);
|
bmp.fastsetpixel(x,y,ocr_Red);
|
||||||
continue;
|
continue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
if (r > ocr_Limit_Med) and (r < (ocr_Limit_High + 10)) and (g > ocr_Limit_Low - 10) and
|
if (r > ocr_Limit_Med) and (r < (ocr_Limit_High + 10)) and (g > ocr_Limit_Low - 10) and
|
||||||
(b < 20) then
|
(b < 20) then
|
||||||
begin
|
begin
|
||||||
bmp.fastsetpixel(x,y,ocr_Green);
|
bmp.fastsetpixel(x,y,ocr_Green);
|
||||||
continue;
|
continue;
|
||||||
end;
|
end;
|
||||||
//shadow
|
|
||||||
|
// Match Shadow as well. We will need it later.
|
||||||
if (r < ocr_Limit_Low) and (g < ocr_Limit_Low) and (b < ocr_Limit_Low) then
|
if (r < ocr_Limit_Low) and (g < ocr_Limit_Low) and (b < ocr_Limit_Low) then
|
||||||
begin
|
begin
|
||||||
bmp.FastSetPixel(x,y, ocr_Purple);
|
bmp.FastSetPixel(x,y, ocr_Purple);
|
||||||
continue;
|
continue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// Black if no match
|
||||||
bmp.fastsetpixel(x,y,0);
|
bmp.fastsetpixel(x,y,0);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{
|
||||||
// make outline black for shadow characteristics filter
|
Make outline black for shadow characteristics filter
|
||||||
// first and last horiz line = 0
|
First and last horiz line = 0. This makes using the shadow algo easier.
|
||||||
|
We don't really throw away information either since we already took the
|
||||||
|
bitmap a bit larger than required.
|
||||||
|
}
|
||||||
for x := 0 to bmp.width -1 do
|
for x := 0 to bmp.width -1 do
|
||||||
bmp.fastsetpixel(x,0,0);
|
bmp.fastsetpixel(x,0,0);
|
||||||
for x := 0 to bmp.width -1 do
|
for x := 0 to bmp.width -1 do
|
||||||
bmp.fastsetpixel(x,bmp.height-1,0);
|
bmp.fastsetpixel(x,bmp.height-1,0);
|
||||||
// same for vertical lines
|
|
||||||
|
// Same for vertical lines
|
||||||
for y := 0 to bmp.Height -1 do
|
for y := 0 to bmp.Height -1 do
|
||||||
bmp.fastsetpixel(0, y, 0);
|
bmp.fastsetpixel(0, y, 0);
|
||||||
for y := 0 to bmp.Height -1 do
|
for y := 0 to bmp.Height -1 do
|
||||||
@ -288,7 +328,6 @@ end;
|
|||||||
on characteristics.
|
on characteristics.
|
||||||
|
|
||||||
For the uptext, a few things apply...
|
For the uptext, a few things apply...
|
||||||
First of all:
|
|
||||||
|
|
||||||
*** Remove False Shadow ***
|
*** Remove False Shadow ***
|
||||||
if shadow[x,y] then not shadow[x-1,y-1]
|
if shadow[x,y] then not shadow[x-1,y-1]
|
||||||
@ -300,7 +339,6 @@ end;
|
|||||||
will soon see that this algorithm will be a *lot* more efficient if we
|
will soon see that this algorithm will be a *lot* more efficient if we
|
||||||
start at the right bottom, instead of the left top. Which means we should
|
start at the right bottom, instead of the left top. Which means we should
|
||||||
work with x-1 and y-1, rather than x+1,y+1
|
work with x-1 and y-1, rather than x+1,y+1
|
||||||
Yeah.... My comments are vague.
|
|
||||||
)
|
)
|
||||||
|
|
||||||
*** UpText chars identity 1 and 2 ***
|
*** UpText chars identity 1 and 2 ***
|
||||||
@ -314,11 +352,14 @@ procedure TMOCR.FilterUpTextByCharacteristics(bmp: TMufasaBitmap; w,h: integer);
|
|||||||
var
|
var
|
||||||
x,y: Integer;
|
x,y: Integer;
|
||||||
begin
|
begin
|
||||||
// Filter 2
|
{ Filter 2
|
||||||
// This performs a `simple' filter.
|
This performs a `simple' filter.
|
||||||
// What we are doing here is simple checking that if Colour[x,y] is part
|
What we are doing here is simple checking that if Colour[x,y] is part
|
||||||
// of the uptext, then so must Colour[x+1,y+1], or Colour[x+1,y+1] is a shadow.
|
of the uptext, then so must Colour[x+1,y+1], or Colour[x+1,y+1] is a shadow.
|
||||||
// if it is neither, we can safely remove it.
|
if it is neither, we can safely remove it.
|
||||||
|
|
||||||
|
XXX: This causes the previously mentioned fuckup.
|
||||||
|
}
|
||||||
for y := 0 to bmp.Height - 2 do
|
for y := 0 to bmp.Height - 2 do
|
||||||
for x := 0 to bmp.Width - 2 do
|
for x := 0 to bmp.Width - 2 do
|
||||||
begin
|
begin
|
||||||
@ -326,11 +367,20 @@ begin
|
|||||||
continue;
|
continue;
|
||||||
if bmp.fastgetpixel(x,y) = clBlack then
|
if bmp.fastgetpixel(x,y) = clBlack then
|
||||||
continue;
|
continue;
|
||||||
if (bmp.fastgetpixel(x,y) <> bmp.fastgetpixel(x+1,y+1)) and (bmp.fastgetpixel(x+1,y+1) <> clpurple) then
|
if (bmp.fastgetpixel(x,y) <> bmp.fastgetpixel(x+1,y+1)) and
|
||||||
|
(bmp.fastgetpixel(x+1,y+1) <> clpurple) then
|
||||||
bmp.fastsetpixel(x,y,{clAqua}0);
|
bmp.fastsetpixel(x,y,{clAqua}0);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Remove false shadow
|
{
|
||||||
|
Remove false shadow. (A shadow isn't larger than 1 pixel. So if x, y is
|
||||||
|
shadow, thex x+1, y+1 can't be shadow. If it is, we can safely remove it.
|
||||||
|
(As well as x+2, y+2, etc).
|
||||||
|
|
||||||
|
The tricky part of the algorithm is that it starts at the bottom,
|
||||||
|
removing shadow point x,y if x-1,y-1 is also shadow.
|
||||||
|
}
|
||||||
|
|
||||||
for y := bmp.Height - 1 downto 1 do
|
for y := bmp.Height - 1 downto 1 do
|
||||||
for x := bmp.Width - 1 downto 1 do
|
for x := bmp.Width - 1 downto 1 do
|
||||||
begin
|
begin
|
||||||
@ -346,24 +396,24 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
// Now we do another filter, with uptext chars identity 1 and 2.
|
// Now we do another filter, with uptext chars identity 1 and 2.
|
||||||
for y := bmp.Height - 2 downto 0 do
|
for y := bmp.Height - 2 downto 0 do
|
||||||
for x := bmp.Width - 2 downto 0 do
|
for x := bmp.Width - 2 downto 0 do
|
||||||
begin
|
begin
|
||||||
if bmp.fastgetpixel(x,y) = clPurple then
|
if bmp.fastgetpixel(x,y) = clPurple then
|
||||||
continue;
|
continue;
|
||||||
if bmp.fastgetpixel(x,y) = clBlack then
|
if bmp.fastgetpixel(x,y) = clBlack then
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// identity 1
|
// identity 1
|
||||||
if (bmp.fastgetpixel(x,y) = bmp.fastgetpixel(x+1,y+1) ) then
|
if (bmp.fastgetpixel(x,y) = bmp.fastgetpixel(x+1,y+1) ) then
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// identity 2
|
// identity 2
|
||||||
if bmp.fastgetpixel(x+1,y+1) <> clPurple then
|
if bmp.fastgetpixel(x+1,y+1) <> clPurple then
|
||||||
begin
|
begin
|
||||||
bmp.fastsetpixel(x,y,clOlive);
|
bmp.fastsetpixel(x,y,clOlive);
|
||||||
continue;
|
continue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// If we make it to here, it means the pixel is part of the uptext.
|
// If we make it to here, it means the pixel is part of the uptext.
|
||||||
end;
|
end;
|
||||||
@ -505,7 +555,7 @@ begin
|
|||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
|
|
||||||
// Filter 1
|
// Filter 1
|
||||||
FilterUpTextByColour(bmp,w,h);
|
FilterUpTextByColour(bmp);
|
||||||
{$IFDEF OCRSAVEBITMAP}
|
{$IFDEF OCRSAVEBITMAP}
|
||||||
bmp.SaveToFile(OCRDebugPath + 'ocrcol.bmp');
|
bmp.SaveToFile(OCRDebugPath + 'ocrcol.bmp');
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
@ -542,8 +592,8 @@ begin
|
|||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// We should make a different TPA
|
// We should make a different TPA
|
||||||
// for each colour, rather than put them all in one. Noise can be a of a
|
// for each colour, rather than put them all in one. Noise can be of a
|
||||||
// differnet colour.
|
// different colour.
|
||||||
setlength(chars, charsbmp.height * charsbmp.width);
|
setlength(chars, charsbmp.height * charsbmp.width);
|
||||||
charscount:=0;
|
charscount:=0;
|
||||||
for y := 0 to charsbmp.height - 1 do
|
for y := 0 to charsbmp.height - 1 do
|
||||||
|