Welcome, Guest
Username: Password: Remember me
  • Page:
  • 1
  • 2

TOPIC:

Resize pictures 14 Jan 2021 13:51 #17133

  • Horst
  • Horst's Avatar
  • Topic Author


  • Posts: 293
  • Hallo
    I need a tool to reduce the size of a picture and i found one in C#.
    Now i translated it to xSharp and it works. But i am not sure if i realy did it well or if i will run into troubles. Because C# has many things i dont understand.
    Can one of you check it ? The prg includes the c# code also is in the attachement.

    Thanks Horst

    Please Log in or Create an account to join the conversation.

    Resize pictures 14 Jan 2021 14:27 #17134

    • SHirsch
    • SHirsch's Avatar


  • Posts: 279
  • Hello Horst,

    you just ignored the using statement.

    BEGIN USING oImage ....
      BEGIN USING  oNewImage ....
         oNewImage:Save....
      END USING
    END USING

    BEGIN USING oGraphics ....
       oGraphics:Draw....
    END USING

    Otherwise the native resources will not be freed. So you built a classic memory leak.
    You could also call oImage:Dispose at the end (and for oImage and oGrahics).

    Stefan

    Please Log in or Create an account to join the conversation.

    Last edit: by SHirsch.

    Resize pictures 14 Jan 2021 14:47 #17136

    • Horst
    • Horst's Avatar
    • Topic Author


  • Posts: 293
  • Hello Stefan
    Yes, because of this Using i wasnt sure and scared. So i changed it to :

    FUNCTION ScaleAndSave(cFileIn AS STRING, cFileOut AS STRING) AS VOID
        LOCAL oImage        AS Image
        LOCAL oNewImage        AS Image
        
        BEGIN USING    oImage         := Image:FromFile(cFileIn)
            BEGIN USING oNewImage     := ScaleImage(oImage, 350, 350)
                oNewImage:Save(cFileOut , ImageFormat.Jpeg)
            END USING
        END USING
                               
        RETURN NIL

    FUNCTION ScaleImage(oImage AS Image, maxWidth AS FLOAT, maxHeight AS FLOAT)
        LOCAL oGraphics        AS Graphics
        LOCAL oNewImage     AS Bitmap
        LOCAL newWidth        AS INT
        LOCAL newHeight        AS INT
        LOCAL ratioX        AS FLOAT
        LOCAL ratioY        AS FLOAT
        LOCAL ratio         AS USUAL

        ratioX         := maxWidth / oImage:Width
        ratioY         := maxHeight / oImage:Height
        ratio         := Math.Min(ratioX, ratioY)
        newWidth     := Integer (oImage:Width * ratio)
        newHeight     := Integer (oImage:Height * ratio)
        oNewImage     := Bitmap{newWidth, newHeight}

        BEGIN USING oGraphics     := Graphics.FromImage(oNewImage)
            oGraphics:DrawImage(oImage, 0, 0, newWidth, newHeight)
        END USING

        RETURN oNewImage

    i will use it with USING, so i have a example in my code. But i prefere the Dispose() like
    Aclass := yx{parameter}
    Aclass:Do()
    Aclass:Dispose()
    For me its more readable.

    Thanks a lot
     

    Please Log in or Create an account to join the conversation.

    Resize pictures 14 Jan 2021 14:52 #17137

    • Horst
    • Horst's Avatar
    • Topic Author


  • Posts: 293
  • Still a little worried  about Local
    Once i learned a local variable will be removed after the return statement of a function .
    So this is not allways true ?

    Horst

    Please Log in or Create an account to join the conversation.

    Resize pictures 14 Jan 2021 14:59 #17138

    • SHirsch
    • SHirsch's Avatar


  • Posts: 279
  • What do you mean?
    In ScaleAndSave you cannot access the local variable oGraphics whichs is using in ScaleImage. You can use oNewImage which is created in ScaleImage because this is your return value. The name of the variable in ScaleAndSave does not have to be oNewImage (which is the same as in ScaleImage). This could be called oScaledImage. So the reference of oNewImage in ScaleImage and oScaledImage (or in your case also oNewImage) in ScaleAndSave is the same.
    Maybe this was your question?

    Stefan

    Please Log in or Create an account to join the conversation.

    Resize pictures 14 Jan 2021 15:11 #17139

    • FFF
    • FFF's Avatar


  • Posts: 1422
  •  LOCAL oImage        AS Image
     LOCAL oNewImage        AS Image
    ....
    Return
    I think he expected both locals to be destroyed after the Return by GC
    Regards
    Karl (X# 2.16.0.5; Xide 2.16; W8.1/64 German)

    Please Log in or Create an account to join the conversation.

    Resize pictures 14 Jan 2021 15:17 #17140

    • Horst
    • Horst's Avatar
    • Topic Author


  • Posts: 293
  • Hi FFF
    Yes thats what i expect

    Please Log in or Create an account to join the conversation.

    Resize pictures 14 Jan 2021 15:59 #17144

    • SHirsch
    • SHirsch's Avatar


  • Posts: 279
  • Horst,

    the object is not destroyed because the reference is returned and still used in the calling method. When the calling method returns  the object will be destroyed. Objects will be destroyed if it is not referenced any more. 

    Stefan

    Please Log in or Create an account to join the conversation.

    Resize pictures 14 Jan 2021 16:03 #17145

    • FFF
    • FFF's Avatar


  • Posts: 1422
  • Stefan,
    the locals ARE in the calling method, so after leaving the caller, they should be destroyed. If so, why the need for Using in ScaleAndSave()?
    Regards
    Karl (X# 2.16.0.5; Xide 2.16; W8.1/64 German)

    Please Log in or Create an account to join the conversation.

    Resize pictures 14 Jan 2021 16:17 #17146

    • SHirsch
    • SHirsch's Avatar


  • Posts: 279
  • Than I did not understand the question.

    A managed object is removed from memory when it's not referenced anymore.
    This is the case for both variables in ScaleAndSave (oImage and oNewImage).
    BUT in ScaleImage the object 'oNewImage' is NOT destroyed after returning to ScaleAndSave. Otherwise it could'nt be used in ScaleAndSave.

    BEGIN USING ensures that 'Dispose' is called at the end of the USING block (even if an error is thrown). You can call Dispose manually as Horst has written. But the managed object still remains in memory until it is not referenced anymore. Dispose just frees all unmanaged resources that are used internally. In ScaleAndSave the object oNewImage will be destroyed after returning to the calling method.

    Please Log in or Create an account to join the conversation.

    Last edit: by SHirsch.

    Resize pictures 14 Jan 2021 20:13 #17153

    • Chris
    • Chris's Avatar


  • Posts: 3984
  • ^^^ It will be automatically destroyed, when the Garbage Collector decides to kick in, to be precise, and this could potentially take a lot of time. Manually destroying objects that are not longer needed releases the resources they use right now and frees memory more quickly.
    XSharp Development Team
    chris(at)xsharp.eu

    Please Log in or Create an account to join the conversation.

    Resize pictures 14 Jan 2021 20:23 #17154

    • FFF
    • FFF's Avatar


  • Posts: 1422
  •  Agreed. But, OTOH, memory in itself isn't a value, as long as it is not in use - the GC will and has to kick in, if there's some threshold crossed.
    Regards
    Karl (X# 2.16.0.5; Xide 2.16; W8.1/64 German)

    Please Log in or Create an account to join the conversation.

    Last edit: by FFF.

    Resize pictures 15 Jan 2021 11:21 #17166

    • Chris
    • Chris's Avatar


  • Posts: 3984
  • It could be unmanaged resources though, which the GC has no idea that they are related to the managed objects. A very small and simple .Net object could have opened a thousand unmanaged file handles, allocate large chunks of unmanaged memory with Win32 API that the GC has no idea about. Especially in a tight loop, as in the sample below, you can end up allocating too many resources, bringing the PC to a crawl, before the GC suddenly kicks in and tries to release everything (collect all objects created) in one go:

    FUNCTION Start() AS VOID
    LOCAL oObject AS SimpleClass
    FOR LOCAL n := 1 AS INT UPTO 1000
        oObject := SimpleClass{n}
        oObject: DoSomeWorkWithIt()
        // all of the above 1000 objects are still alive in memory, until the GC kills them all together
    NEXT
    
    CLASS SimpleClass IMPLEMENTS IDisposable
        CONSTRUCTOR(nID AS INT)
            SELF:AllocateHugeAmountOfResources()
        DESTRUCTOR()
            SELF: Dispose()
        METHOD Dispose() AS VOID
            SELF:FreeHugeAmountOfResources()
    END CLASS


    But if you added a oObject: Dispose() at the end of each iteration, resources would be released exactly at the time they are not needed anymore and memory consumption would always remain at the minimum possible.

    Even worse, such an object could still be referenced accidentally while is not needed anymore, thus making it impossible to collect it:
    FUNCTION Start() AS VOID
    LOCAL oObject1,oObject2 AS SimpleClass
    oObject1 := SimpleClass{1}
    oObject2 := SimpleClass{2}
    oObject1:DoSomeWorkWithIt()
    oObject2:DoSomeWorkWithIt()
    
    //
    // <a lot of other code in here, having nothing to do with those objects>
    //
    
    // this line causes those two objects and their resources
    // to still be kept alive till here by the Garbage Collector
    
    ? oObject1:GetResult() + oObject2:GetResult()


    But, by using BEGIN USING,instead you can limit the visibility of the variables to only where they are really needed, force yourself to write more efficient code and make resource management much more efficient:
    FUNCTION Start() AS VOID
    LOCAL nResult AS INT
    BEGIN USING VAR oObject1 := SimpleClass{1}
        oObject1:DoSomeWorkWithIt()
        nResult := oObject1:GetResult()
    END USING
    // The resources used by oObject1 have already been freed here, because
    // Dispose() was automatically called from the BEGIN...END USING statement
    // Also oObject1 is not visible here anymore, so it can not be used accidentally
    // and the GC can also completely collect it
    
    BEGIN USING VAR oObject2 := SimpleClass{2}
        oObject2:DoSomeWorkWithIt()
        nResult += oObject2:GetResult()
    END USING
    // same for oObject2, it can be collected now
    
    // <a lot of other code in here, having nothing to do with those objects>


    When BEGIN USING is used this way, it both limits the scope of the variable and also ensures Dispose() is automatically called, leading to the more "clean" results.

    Btw, I am a bit bothered you cannot use my favorite "LOCAL" syntax for BEGIN USING above, will log this.
     
    XSharp Development Team
    chris(at)xsharp.eu

    Please Log in or Create an account to join the conversation.

    Last edit: by Chris.

    Resize pictures 16 Jan 2021 08:52 #17182

    • Horst
    • Horst's Avatar
    • Topic Author


  • Posts: 293
  • Hi Chris
    To be sure.
    So this Code is not right ? BTW it works (the Local)

    LOCAL oGraphics        AS Graphics
    BEGIN USING oGraphics     := Graphics.FromImage(oNewImage)
           oGraphics:DrawImage(oImage, 0, 0, newWidth, newHeight)
    END USING

    I have to change it to Begin Using VAR oGraphics and removing the LOCAL Statement    ?

    And:
            // PersonenIndex erstellen
            cFilename := SELF:gdbPerson
            odb := _cryptserver {WorkDir () + SELF:cPathRoot+cFilename,TRUE,FALSE,"DBFCDX"}
            // 1
                lOrderOk := odb:createorder (    "Id_Person",,"Id_Person"    ,{|| _Field->Id_Person}    ,    FALSE,FALSE)
                    IF  ! lOrderOk ; SELF:WriteCgiLog ("Person, Id_Person Index ERROR !") ; ENDIF
    > Hier i have to insert the ODB:DISPOSE () ??  <
            // Personen Zusatz Index erstellen
            cFilename := SELF:gdbpZusatz
            odb := _cryptserver {WorkDir () + SELF:cPathRoot+cFilename,TRUE,FALSE,"DBFCDX"}
            // 1
                lOrderOk := odb:createorder (    "Id_Person",,"Id_Person+Nachname"    ,{|| _Field->Id_Person+_Field->Nachname}    ,    FALSE,FALSE)
                    IF  ! lOrderOk ; SELF:WriteCgiLog ("pZusatz, Id_Person Index ERROR !") ; ENDIF

    Horst




     

    Please Log in or Create an account to join the conversation.

    Resize pictures 16 Jan 2021 10:24 #17184

    • Chris
    • Chris's Avatar


  • Posts: 3984
  • Hi Horst,

    The code with LOCAL and BEGIN USING is still right, only "issue" is that you can still (accidentally) use the oGraphics var even after the END USING line, where the object has been disposed, so does not make sense reusing it (except if you instantiate it again). If you use the VAR syntax, the variable wil be visible only inside the BEGIN...END construct, attempting to use it below will lead to a compiler error, thus preventing possible programmer mistakes.

    About using Dispose() in your second sample, it's just good programming practice. In this specific sample, I doubt it will make a noticeable difference, it's just considered a good habbit disposing objects that you no longer needed. If you are used to doing it even in places where it is not really important, you will be doing it also in places where it can make a big difference.
    XSharp Development Team
    chris(at)xsharp.eu

    Please Log in or Create an account to join the conversation.

    Resize pictures 19 Jan 2021 08:02 #17218

    • Horst
    • Horst's Avatar
    • Topic Author


  • Posts: 293
  • Hi Chris

    But it looks like DbServer has no Dispose()  Methode, cant compile.

    Horst

    Please Log in or Create an account to join the conversation.

    Resize pictures 19 Jan 2021 09:07 #17219

    • Chris
    • Chris's Avatar


  • Posts: 3984
  • Hi Horst,

    Not every class has a Dispose() method, it's only those that do allocate external resources and it makes sense to free them ASAP that have it. DBServer's only external resource it uses is to open a dbf file, and there's already a method to dispose that resource, it's just called Close() instead of Dispose() and of course you are most probably calling it already.

    The DBServer class was designed more than 25 years ago, when we did not have IDispose interfaces etc, but Close() is indeed working very similarly to Dispose(), in the sense you can also omit calling it, and just let the object go out of scope, wait until the GC collects it, which then calls it Destructor() (Axit() in VO), which in turn calls Close() in order to close the assosiated file. It would just take more time to close the file this way, until there are zero references to this DBServer object anymore and the GC kicks in.

    Maybe we should actually now introduce a Dispose() method in DBServer class, which would simply call Close(), to make it more modern and allow DBServer to be used with BEGIN...END USING.
    XSharp Development Team
    chris(at)xsharp.eu

    Please Log in or Create an account to join the conversation.

    Resize pictures 19 Jan 2021 09:50 #17221

    • Horst
    • Horst's Avatar
    • Topic Author


  • Posts: 293
  • Hi Chris

    I think the Dispose for dbServer is not needed, only a comment in the help file that Close will call Dispose, Close is more readable.

    Horst

    Please Log in or Create an account to join the conversation.

    Resize pictures 19 Jan 2021 10:44 #17222

    • wriedmann
    • wriedmann's Avatar


  • Posts: 3367
  • Hi Chris,
    adding a Dispose() method in the DBServer class makes definitively sense because DBServers can be used also in non-legacy code.
    I had tried to build a CoreDBFServer class that does not uses DBServer internally, but have failed.
    Maybe we can start that together: a DBFServer class that is usable in non VO code.
    Wolfgang
    Wolfgang Riedmann
    Meran, South Tyrol, Italy

    www.riedmann.it - docs.xsharp.it

    Please Log in or Create an account to join the conversation.

    Resize pictures 19 Jan 2021 12:24 #17224

    • Chris
    • Chris's Avatar


  • Posts: 3984
  • Horst: Dispose() and destructor are different things. Dispose() is just a name, by convention classes in .Net that need to release resources implement a method with that name, but it could be any other name really. There is no Dispose() method at all in DBServer, there is only Close(), which is called either by the programmer directly, or by the garbage collector when it collects (destructs) the object (Close() is called from the destructor()).

    Wolfgang: What do you mean you failed? Do you mean it was too much work? You could maybe just use the original code of DBServer and adjust it to not use USAULs or other VO types (step by step of course, as it is a lot of code).
    XSharp Development Team
    chris(at)xsharp.eu

    Please Log in or Create an account to join the conversation.

    • Page:
    • 1
    • 2