#lang racket/base
(require racket/class
         ffi/unsafe
         ffi/unsafe/alloc
         racket/draw
         racket/draw/private/local
         racket/draw/unsafe/cairo
         "../../lock.rkt"
         racket/draw/unsafe/bstr
         "utils.rkt"
         "types.rkt"
         (only-in '#%foreign ffi-callback))

(provide 
 (protect-out bitmap->pixbuf
              pixbuf->bitmap
              
              _GdkPixbuf
              gtk_image_new_from_pixbuf
              release-pixbuf))

(define _GdkPixbuf (_cpointer/null 'GdkPixbuf))

(define release-pixbuf ((deallocator) g_object_unref))

(define-gtk gtk_image_new_from_pixbuf (_fun _GdkPixbuf -> _GtkWidget))
(define-gdk_pixbuf gdk_pixbuf_new_from_data (_fun _pointer ; data
						  _int ; 0  =RGB
						  _gboolean ; has_alpha?
						  _int ; bits_per_sample
						  _int ; width
						  _int ; height
						  _int ; rowstride
						  _fpointer ; destroy
						  _pointer  ; destroy data
						  -> _GdkPixbuf)
  #:wrap (allocator release-pixbuf))

(define-gdk gdk_cairo_set_source_pixbuf (_fun _cairo_t _GdkPixbuf _double* _double* -> _void))
(define-gdk_pixbuf gdk_pixbuf_get_width (_fun _GdkPixbuf -> _int))
(define-gdk_pixbuf gdk_pixbuf_get_height (_fun _GdkPixbuf -> _int))

(define free-it (ffi-callback free
                              (list _pointer)
                              _void
                              #f
                              #t))

(define (bitmap->pixbuf orig-bm [scale 1.0])
  (let* ([w (send orig-bm get-width)]
         [h (send orig-bm get-height)]
	 [sw (ceiling (inexact->exact (* scale w)))]
         [sh (ceiling (inexact->exact (* scale h)))]
         [str (make-bytes (* sw sh 4) 255)])
    (define-values (bm unscaled? usw ush)
      (cond
       [(= scale 1.0) (values orig-bm #f w h)]
       [(= scale (send orig-bm get-backing-scale)) (values orig-bm #t sw sh)]
       [else (values (rescale orig-bm scale) #f sw sh)]))
    (send bm get-argb-pixels 0 0 usw ush str #f #:unscaled? unscaled?)
    (let ([mask (send bm get-loaded-mask)])
      (when mask
        (send mask get-argb-pixels 0 0 usw ush str #t #:unscaled? unscaled?)))
    (atomically
     (let ([rgba (malloc (* sw sh 4) 'raw)])
       (memcpy rgba (ptr-add str 1) (sub1 (* sw sh 4)))
       (for ([i (in-range 0 (* sw sh 4) 4)])
         (ptr-set! rgba _byte (+ i 3) (bytes-ref str i)))
       (gdk_pixbuf_new_from_data rgba
                                 0
                                 #t
                                 8
                                 sw
                                 sh
                                 (* sw 4)
                                 free-it
                                 #f)))))

(define (pixbuf->bitmap pixbuf)
  (let* ([w (gdk_pixbuf_get_width pixbuf)]
         [h (gdk_pixbuf_get_height pixbuf)]
         [bm (make-object bitmap% w h #f #t)]
         [s (send bm get-cairo-surface)]
         [cr (cairo_create s)])
    (gdk_cairo_set_source_pixbuf cr pixbuf 0 0)
    (cairo_rectangle cr 0 0 w h)
    (cairo_fill cr)
    (cairo_destroy cr)
    bm))

(define (rescale bm scale)
  (define w (send bm get-width))
  (define h (send bm get-height))
  (define new-bm (make-bitmap (ceiling (inexact->exact (* scale w)))
			      (ceiling (inexact->exact (* scale h)))))
  (define dc (send new-bm make-dc))
  (send dc set-scale scale scale)
  (send dc set-smoothing 'smoothed)
  (send dc draw-bitmap bm 0 0)
  new-bm)
