rageworx / fltk-custom

A clone of FLTK 1.4.0 for enhance as customized GUI, more functional. This project is belong to https://fltk.org.
https://rageworx.info
Other
8 stars 1 forks source link

User defined Fl_Image scaling method #58

Open rageworx opened 11 months ago

rageworx commented 11 months ago

According to this issue, https://github.com/fltk/fltk/issues/742 Windows don't provided any scaling method when high DPI case.

Looks a custom version may have this type of callback,

 The scaling algorithm to use for RGB images.
*/
enum Fl_RGB_Scaling {
  FL_RGB_SCALING_NEAREST = 0, ///< default RGB image scaling algorithm
#ifndef FLTK_EXT_VERSION
  FL_RGB_SCALING_BILINEAR     ///< more accurate, but slower RGB image scaling algorithm
#else
  FL_RGB_SCALING_BILINEAR = 1,
  FL_RGB_SCALING_USER
#endif /// of FLTK_EXT_VERSION    
};

#ifdef FLTK_EXT_VERSION
/** User scaling callback type definition for FL_RGB_SCALING_USER */
typedef void (Fl_Image_UserScale)(Fl_RGB_Image*, int w, int h, Fl_RGB_Image*);
typedef Fl_Image_UserScale* Fl_Image_UserScale_p; // needed for BORLAND
#endif /// of FLTK_EXT_VERSION    

...

#ifndef FLTK_EXT_VERSION
  static void scaling_algorithm(Fl_RGB_Scaling algorithm) {scaling_algorithm_ = algorithm; }
#else 
  static void scaling_algorithm(Fl_RGB_Scaling algorithm, Fl_Image_UserScale* userscale = NULL) 
  {
      scaling_algorithm_ = algorithm;
      user_scaling_ = userscale;
  }
#endif /// of FLTK_EXT_VERSION    
rageworx commented 11 months ago

Support limits

rageworx commented 11 months ago

Changes of Fl_Image.H

ifdef FLTK_EXT_VERSION

/ User scaling callback type definition for FL_RGB_SCALING_USER / typedef void (Fl_Image_UserScale)(Fl_RGB_Image, int, int, int, int, Fl_RGB_Image); typedef Fl_Image_UserScale* Fl_Image_UserScale_p; // needed for BORLAND

endif /// of FLTK_EXT_VERSION

* and in a Fl_Image.H class

class FL_EXPORT Fl_Image { friend class Fl_Graphics_Driver; public: static const int ERR_NO_IMAGE = -1; static const int ERR_FILE_ACCESS = -2; static const int ERR_FORMAT = -3; static const int ERR_MEMORY_ACCESS = -4;

private: int w, h, d, ld, count_; int dataw, datah; const char const data_; static Fl_RGB_Scaling RGBscaling; // method used when copying RGB images static Fl_RGB_Scaling scalingalgorithm; // method used to rescale RGB source images before drawing

ifdef FLTK_EXT_VERSION

static Fl_Image_UserScale_p userscaling; /// user scaling callback method.

endif /// of FLTK_EXT_VERSION

... /* Sets what algorithm is used when resizing a source image to draw it. The default algorithm is FL_RGB_SCALING_BILINEAR. Drawing an Fl_Image is sometimes performed by first resizing the source image and then drawing the resized copy. This occurs, e.g., when drawing to screen under X11 without Xrender support after having called scale(). This function controls what method is used when the image to be resized is an Fl_RGB_Image. \version 1.4 /

ifndef FLTK_EXT_VERSION

static void scaling_algorithm(Fl_RGB_Scaling algorithm) {scalingalgorithm = algorithm; }

else

/* In version of FLTK-custom provides FL_RGB_SCALING_USER with userscale callback method, to better image quality. userscale only needed when algorithm set to FL_RGB_SCALING_USER. / static void scaling_algorithm(Fl_RGB_Scaling algorithm, Fl_Image_UserScale* userscale = NULL) { scalingalgorithm = algorithm; userscaling = userscale; }

static Fl_Image_UserScale* user_scaling_algorithm() { return userscaling; }

endif /// of FLTK_EXT_VERSION

rageworx commented 11 months ago

Changes of Fl_GDI_Graphics_Driver_Image.cxx

rageworx commented 11 months ago

Implementation

void usr_scale(Fl_RGB_Image* s, int x, int y, int w, int h, Fl_RGB_Image** o)
{
    printf( "(debug)usr_scale( %p, %d, %d, %d, %d, .. );\n",
            s, x, y, w, h );

    if ( s != NULL )
    {
        printf( "(debug)s->w() = %d, s->h() = %d\n", s->w(), s->h() );
        Fl_RGB_Image* r = fl_imgtk::rescale( s, w, h, fl_imgtk::BICUBIC );
        printf( "(debug)o->w() = %d, o->h() = %d\n", r->w(), r->h() );
        *o = r;
    }

    fflush( stdout );
}

void presetFLTKenv()
{
    Fl::set_font( FL_FREE_FONT, convLng );
    Fl_Double_Window::default_xclass( DEF_APP_CLSNAME );

#ifdef FLTK_EXT_VERSION
    Fl::scheme( "flat" );

    Fl_Image:: scaling_algorithm( FL_RGB_SCALING_USER, usr_scale );
#else
    Fl::scheme( "gtk+" );
    // Default RGB image scaling for high-DPI, but not works on Windows.
    Fl_Image::scaling_algorithm( FL_RGB_SCALING_BILINEAR );
#endif /// of FLTK_EXT_VERSION

}

Debugging Status,

rageworx commented 11 months ago

Fixed draw_rgb()

void Fl_GDI_Graphics_Driver::draw_rgb(Fl_RGB_Image *rgb, int XP, int YP, int WP, int HP, int cx, int cy) {
  if (Fl_Graphics_Driver::start_image(rgb, XP, YP, WP, HP, cx, cy, XP, YP, WP, HP)) {
    return;
  }
  if ((rgb->d() % 2) == 0 && !fl_can_do_alpha_blending()) {
    Fl_Graphics_Driver::draw_rgb(rgb, XP, YP, WP, HP, cx, cy);
    return;
  }
  if (!*Fl_Graphics_Driver::id(rgb)) {
    cache(rgb);
  }
  push_clip(XP, YP, WP, HP);
  XP -= cx; YP -= cy;
  WP = rgb->w(); HP = rgb->h();
  cache_size(rgb, WP, HP);
  HDC new_gc = CreateCompatibleDC(gc_);
  int save = SaveDC(new_gc);
  SelectObject(new_gc, (HBITMAP)*Fl_Graphics_Driver::id(rgb));
#ifdef FLTK_EXT_VERSION
  int sclsucc = 0;
  Fl_Image_UserScale_p usrscl = Fl_Image::user_scaling_algorithm();
  if ( (Fl_Image::scaling_algorithm() == FL_RGB_SCALING_USER) && (usrscl != NULL ) ) {
    Fl_RGB_Image* sclrgb = NULL;    
    usrscl( rgb, this->floor(XP), this->floor(YP), WP, HP, &sclrgb );
    if ( sclrgb != NULL ) {
      cache(sclrgb);
      //WP = sclrgb->w();
      //HP = sclrgb->h();
      //cache_size(rgb, WP, HP);
      SelectObject(new_gc, (HBITMAP)*Fl_Graphics_Driver::id(sclrgb));
      if ( (sclrgb->d() % 2) == 0 ) {
        alpha_blend_(this->floor(XP), this->floor(YP), WP, HP, new_gc, 0, 0, sclrgb->data_w(), sclrgb->data_h());
      } else {
        SetStretchBltMode(gc_, HALFTONE);
        StretchBlt(gc_, this->floor(XP), this->floor(YP), WP, HP, new_gc, 0, 0, sclrgb->data_w(), sclrgb->data_h(), SRCCOPY);
      }
      delete sclrgb;
      DeleteObject(new_gc);

      sclsucc = 1;
    }
  }

  if (sclsucc == 0)
  {
#endif /// of FLTK_EXT_VERSION
    if ( (rgb->d() % 2) == 0 ) {
      alpha_blend_(this->floor(XP), this->floor(YP), WP, HP, new_gc, 0, 0, rgb->data_w(), rgb->data_h());
    } else {
      SetStretchBltMode(gc_, HALFTONE);
      StretchBlt(gc_, this->floor(XP), this->floor(YP), WP, HP, new_gc, 0, 0, rgb->data_w(), rgb->data_h(), SRCCOPY);
    }
#ifdef FLTK_EXT_VERSION  
  }
#endif /// of FLTK_EXT_VERSION

  RestoreDC(new_gc, save);
  DeleteDC(new_gc);
  pop_clip();
}

Testing by scaling up with Ctrl+"+" key to 170%, BICUBIC.

image

rageworx commented 11 months ago

Downscaling

image

rageworx commented 11 months ago

Memory leakage, by user scaling ?

image

rageworx commented 11 months ago

Leakage reason,

Question,

rageworx commented 11 months ago

Leakage trace ...

rageworx commented 11 months ago

Sources are being updated to branch of https://github.com/rageworx/fltk-custom/tree/1.4.0-dev-usr-scaling.

rageworx commented 11 months ago

Leakage solved, and tested.

at https://github.com/rageworx/fltk-custom/commit/ce086caecd4689ba7ed258d5f7ab41bf527dedcc.

rageworx commented 11 months ago

New error reported,

rageworx commented 11 months ago

Error occurs on void Fl_GDI_Graphics_Driver::cache(Fl_RGB_Image *img).

fl_draw_image() signal segment fault ?

    if ((img->d() == 2 || img->d() == 4) && fl_can_do_alpha_blending()) {
      fl_draw_image(img->array, 0, 0, img->data_w(), img->data_h(), img->d()|FL_IMAGE_WITH_ALPHA, img->ld());
    } else {
      fl_draw_image(img->array, 0, 0, img->data_w(), img->data_h(), img->d(), img->ld());
      if (img->d() == 2 || img->d() == 4) {
        *Fl_Graphics_Driver::mask(img) = (fl_uintptr_t)create_alphamask(img->data_w(), img->data_h(), img->d(), img->ld(), img->array);
      }
    }

from ...

cache->1,2,3,4,5,done
usr_scale, 0, 0, 1109, 50 ...
(debug)s(0000013d526a6c80, 1109x50x3) ->scaled?(0) : o(1109x50x3) == 0000013d526a6da0
(debug)draw_rgb, user scale debug ...
(debug) .. caching Fl_RGB_Image(0000013d526a6da0) ...
cache->1,2,3,4,5,done
(debug) ... step #2
(debug) ... step #3
(debug) ... step #4
(debug) ... step #5
(debug) ... step #6
(debug) ... step #7
cache->1,2,3,4,5,done
usr_scale, 22, 12, 25, 25 ...
(debug)s(0000013d5269b870, 25x25x4) ->scaled?(0) : o(25x25x4) == 0000013d526b5fe0
(debug)draw_rgb, user scale debug ...
(debug) .. caching Fl_RGB_Image(0000013d526b5fe0) ...
cache->1,2,%                                                           

Segment fault in ..

fl_draw_image(img->array, 0, 0, img->data_w(), img->data_h(), img->d()|FL_IMAGE_WITH_ALPHA, img->ld());

and it calls to ...

void Fl_Scalable_Graphics_Driver::draw_image(const uchar* buf, int X,int Y,int W,int H, int D, int L) {
  if (scale() == 1) {
    draw_image_unscaled(buf, X,Y,W,H,D,L);
  } else {
    draw_image_rescale((void*)buf, NULL, X, Y, W, H, D, L, false);
  }
}

in Fl_Graphics_Driver -> Fl_GDI_Graphics_Driver_Image, innards().

rageworx commented 11 months ago

Segment fault caused by null buffer reference in innards().

(debug)void innards( 0000000000000000, 0, 0, 25, 25, delta=4, ld=0, d=4, 0000000000000000, 0000000000000000, HDC=2181115365
rageworx commented 11 months ago

25x25 image may a SVG, and it must be converted to RGBA, but why not contains array ?

rageworx commented 11 months ago

Error fixed, to check sclrgb->array has pointer.

rageworx commented 11 months ago

Final testing almost completed in another commercial project that runs on Windows GDI( and GDI+) and fl_imgtk fixed bug library for refer to Fl_RGB_Image data width and height. Most of changes committed to v1.4.0-dev-user-scaling branch.