summaryrefslogtreecommitdiff
path: root/chickadee/image.scm
blob: 289fb14c7c974e5d09363b80df90a3f268b9a390 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
;;; Chickadee Game Toolkit
;;; Copyright © 2023 David Thompson <dthompson2@worcester.edu>
;;;
;;; Chickadee is free software: you can redistribute it and/or modify
;;; it under the terms of the GNU General Public License as published
;;; by the Free Software Foundation, either version 3 of the License,
;;; or (at your option) any later version.
;;;
;;; Chickadee is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;;; General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with this program.  If not, see
;;; <http://www.gnu.org/licenses/>.

;;; Commentary:
;;
;; Data type for representing images on disk.
;;
;;; Code:

(define-module (chickadee image)
  #:use-module (chickadee graphics color)
  #:use-module (chickadee image jpeg)
  #:use-module (chickadee image png)
  #:use-module (chickadee pixbuf)
  #:use-module (chickadee utils)
  #:use-module (ice-9 exceptions)
  #:use-module (ice-9 match)
  #:use-module (rnrs bytevectors)
  #:use-module (srfi srfi-9)
  #:use-module (srfi srfi-9 gnu)
  #:export (make-image
            image?
            image-file-name
            temp-image-file-name
            read-image
            write-image))

(define &image-error
  (make-exception-type '&image-error &error '()))

(define make-image-error (record-constructor &image-error))

(define (image-error message irritants)
 (raise-exception
  (make-exception
   (make-image-error)
   (make-exception-with-message message)
   (make-exception-with-irritants irritants))))

;; An image is pixel data that is stored on disk in one of a variety
;; of formats.
(define-record-type <image>
  (%make-image file-name)
  image?
  (file-name image-file-name))

(define (make-image file-name)
  (%make-image (absolute-file-name file-name)))

(define (print-image image port)
  ;; Rendering in a format that Geiser understands so that images can
  ;; be viewed inline at the REPL.
  (format port "#<Image: ~a>" (image-file-name image)))

(set-record-type-printer! <image> print-image)

(define (image-format image)
  (match (file-extension (image-file-name image))
    ((or "jpg" "jpeg") 'jpeg)
    ("png" 'png)
    (_ 'unknown)))

(define* (read-image image)
  "Read data from IMAGE and return a pixbuf."
  (define-values (pixels width height)
    (case (image-format image)
      ((jpeg)
       (load-jpeg (image-file-name image)))
      ((png)
       (load-png (image-file-name image)))
      (else
       (image-error "unsupported image format: ~a" (image-format image)))))
  (bytevector->pixbuf pixels width height #:format 'rgba #:bit-depth 8))

;; Not guaranteed to be a file that doesn't exist, but good enough.
(define (temp-image-file-name format)
  (string-append (tmpdir) "/"
                 (symbol->string
                  (gensym "chickadee-"))
                 "."
                 (symbol->string format)))

(define* (write-image pixbuf
                      #:optional (file-name (temp-image-file-name 'png))
                      #:key (format 'png))
  "Write PIXBUF to FILE-NAME and return an image object.  FORMAT may be
either 'png' or 'jpeg'."
  (unless (and (eq? (pixbuf-format pixbuf) 'rgba)
               (= (pixbuf-bit-depth pixbuf) 8))
    (image-error "can only write RGBA pixbufs with 8 bit depth: ~a" pixbuf))
  (case format
    ((png)
     (save-png (pixbuf-pixels pixbuf)
               (pixbuf-width pixbuf)
               (pixbuf-height pixbuf)
               file-name))
    (else
     (image-error "unsupport image format: ~a" format)))
  (make-image file-name))