package mm

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Source file imageCanvas.ml

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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
(*
 * Copyright 2011 The Savonet Team
 *
 * This file is part of ocaml-mm.
 *
 * ocaml-mm is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * ocaml-mm 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 Lesser General Public License
 * along with ocaml-mm; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * As a special exception to the GNU Library General Public License, you may
 * link, statically or dynamically, a "work that uses the Library" with a publicly
 * distributed version of the Library to produce an executable file containing
 * portions of the Library, and distribute that executable file under terms of
 * your choice, without any of the additional requirements listed in clause 6
 * of the GNU Library General Public License.
 * By "a publicly distributed version of the Library", we mean either the unmodified
 * Library as distributed by The Savonet Team, or a modified version of the Library that is
 * distributed under the conditions defined in clause 3 of the GNU Library General
 * Public License. This exception does not however invalidate any other reasons why
 * the executable file might be covered by the GNU Library General Public License.
 *
 *)

open ImageBase

module type CanvasImage = sig
  type t

  val width : t -> int
  val height : t -> int
  val size : t -> int
  val create : int -> int -> t
  val blank : t -> unit
  val copy : t -> t
  val add : t -> ?x:int -> ?y:int -> t -> unit
  val has_alpha : t -> bool
  val fill_alpha : t -> int -> unit
  val set_pixel_rgba : t -> int -> int -> Pixel.rgba -> unit
  val randomize : t -> unit
  val scale : t -> t -> unit
end

(** A canvas of images. The structure is immutable but its elements might be
    returned and therefore should not be used in place. *)
module Canvas (I : CanvasImage) = struct
  module Element = struct
    type t = Image of (int * int) * I.t  (** An image at given offset. *)

    let size = function Image (_, img) -> I.size img

    let translate dx dy = function
      | Image ((x, y), img) -> Image ((x + dx, y + dy), img)
  end

  module E = Element

  type t = { width : int; height : int; elements : E.t list }

  let create width height = { width; height; elements = [] }
  let width c = c.width
  let height c = c.height
  let size c = List.fold_left (fun n e -> n + E.size e) 0 c.elements
  let planes c = List.length c.elements

  let make ?width ?height ?(x = 0) ?(y = 0) image =
    let width = Option.value ~default:(I.width image) width in
    let height = Option.value ~default:(I.height image) height in
    { width; height; elements = [E.Image ((x, y), image)] }

  let add c c' =
    (* assert ((c.width < 0 || c.width = c'.width) && (c.height < 0 || c.height = c'.height)); *)
    {
      width = c'.width;
      height = c'.height;
      elements = c.elements @ c'.elements;
    }

  (* TODO: improve precision with something like this:
     https://stackoverflow.com/questions/2628118/rectangles-covering *)
  let covering c =
    let width = width c in
    let height = height c in
    let covering_element = function
      | E.Image ((x, y), img) ->
          let w = I.width img in
          let h = I.height img in
          x <= 0 && y <= 0
          && x + w >= width
          && y + h >= height
          && not (I.has_alpha img)
    in
    List.exists covering_element c.elements

  let render ?(fresh = false) ?(transparent = true) c =
    assert (width c >= 0 && height c >= 0);
    match c.elements with
      | [Image ((0, 0), img)]
        when (not fresh) && I.width img = width c && I.height img = height c ->
          img
      | elements ->
          let r = I.create (width c) (height c) in
          if not (covering c) then (
            I.blank r;
            if transparent then I.fill_alpha r 0);
          let add = function E.Image ((x, y), img) -> I.add img ~x ~y r in
          List.iter_right add elements;
          r

  let rendered ?transparent c = make (render ?transparent c)
  let map f c = make (f (render c))

  let iter f c =
    let img = render ~fresh:true c in
    f img;
    make img

  let translate dx dy c =
    if dx = 0 && dy = 0 then c
    else { c with elements = List.map (E.translate dx dy) c.elements }

  let viewport ?(x = 0) ?(y = 0) width height c =
    translate (-x) (-y) { c with width; height }

  let bounding_box c =
    let p = (width c, height c) in
    let d = (0, 0) in
    List.fold_left
      (fun (p, d) -> function
        | E.Image ((x, y), img) ->
            (Point.min p (x, y), Point.max d (I.width img, I.height img)))
      (p, d) c.elements

  let scale ?(scaler = I.scale) (nx, dx) (ny, dy) c =
    if nx = dx && ny = dy then c
    else (
      let elements =
        List.map
          (function
            | E.Image ((x, y), img) ->
                let scl =
                  I.create (I.width img * nx / dx) (I.height img * ny / dy)
                in
                scaler img scl;
                E.Image ((x * nx / dx, y * ny / dy), scl))
          c.elements
      in
      { width = c.width; height = c.height; elements })

  let resize ?(proportional = true) ?scaler w' h' img =
    let w = width img in
    let h = height img in
    let (nx, dx), (ny, dy) =
      if proportional then (
        let f = Fraction.min (w', w) (h', h) in
        (f, f))
      else ((w', w), (h', h))
    in
    let x, y =
      if proportional then ((w' - (w * nx / dx)) / 2, (h' - (h * ny / dy)) / 2)
      else (0, 0)
    in
    scale ?scaler (nx, dx) (ny, dy) img |> translate x y |> viewport w' h'

  module Draw = struct
    let line color (x1, y1) (x2, y2) =
      let dx = min x1 x2 in
      let dy = min y1 y2 in
      let w = abs (x2 - x1) in
      let h = abs (y2 - y1) in
      let buf = I.create w h in
      I.blank buf;
      I.fill_alpha buf 0;
      Draw.line
        (fun i j ->
          if 0 <= i && i < w && 0 <= j && j < h then
            I.set_pixel_rgba buf i j color)
        (x1 - dx, y1 - dy)
        (x2 - dx, y2 - dy);
      make ~x:dx ~y:dy ~width:(-1) ~height:(-1) buf
  end
end

module CanvasYUV420 = Canvas (struct
  include ImageYUV420

  let create w h = create w h
  let scale = scale ~proportional:false
end)
OCaml

Innovation. Community. Security.