| 1 |
;;; -*- Mode: LISP; Syntax: COMMON-LISP; Package: CL-GD; Base: 10 -*- |
|---|
| 2 |
;;; $Header: /usr/local/cvsrep/gd/misc.lisp,v 1.15 2007/01/01 23:41:00 edi Exp $ |
|---|
| 3 |
|
|---|
| 4 |
;;; Copyright (c) 2003-2007, Dr. Edmund Weitz. All rights reserved. |
|---|
| 5 |
|
|---|
| 6 |
;;; Redistribution and use in source and binary forms, with or without |
|---|
| 7 |
;;; modification, are permitted provided that the following conditions |
|---|
| 8 |
;;; are met: |
|---|
| 9 |
|
|---|
| 10 |
;;; * Redistributions of source code must retain the above copyright |
|---|
| 11 |
;;; notice, this list of conditions and the following disclaimer. |
|---|
| 12 |
|
|---|
| 13 |
;;; * Redistributions in binary form must reproduce the above |
|---|
| 14 |
;;; copyright notice, this list of conditions and the following |
|---|
| 15 |
;;; disclaimer in the documentation and/or other materials |
|---|
| 16 |
;;; provided with the distribution. |
|---|
| 17 |
|
|---|
| 18 |
;;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR 'AS IS' AND ANY EXPRESSED |
|---|
| 19 |
;;; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|---|
| 20 |
;;; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|---|
| 21 |
;;; ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
|---|
| 22 |
;;; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|---|
| 23 |
;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
|---|
| 24 |
;;; GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|---|
| 25 |
;;; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
|---|
| 26 |
;;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|---|
| 27 |
;;; NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|---|
| 28 |
;;; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|---|
| 29 |
|
|---|
| 30 |
(in-package :cl-gd) |
|---|
| 31 |
|
|---|
| 32 |
(defun interlacedp (&optional (image *default-image*)) |
|---|
| 33 |
"Returns whether IMAGE will be stored in an interlaced fashion." |
|---|
| 34 |
(check-type image image) |
|---|
| 35 |
(not (zerop (gd-image-get-interlaced (img image))))) |
|---|
| 36 |
|
|---|
| 37 |
(defun (setf interlacedp) (interlaced &optional (image *default-image*)) |
|---|
| 38 |
"Sets whether IMAGE will be stored in an interlaced fashion." |
|---|
| 39 |
(check-type image image) |
|---|
| 40 |
(gd-image-interlace (img image) (if interlaced 1 0)) |
|---|
| 41 |
interlaced) |
|---|
| 42 |
|
|---|
| 43 |
(defun differentp (image1 image2) |
|---|
| 44 |
"Returns false if the two images won't appear different when |
|---|
| 45 |
displayed. Otherwise the return value is a list of keywords describing |
|---|
| 46 |
the differences between the images." |
|---|
| 47 |
(check-type image1 image) |
|---|
| 48 |
(check-type image2 image) |
|---|
| 49 |
(let ((result (gd-image-compare (img image1) (img image2)))) |
|---|
| 50 |
(cond ((zerop (logand +gd-cmp-image+ result)) |
|---|
| 51 |
nil) |
|---|
| 52 |
(t |
|---|
| 53 |
(loop for (gd-flag keyword) in `((,+gd-cmp-num-colors+ |
|---|
| 54 |
:different-number-of-colors) |
|---|
| 55 |
(,+gd-cmp-color+ |
|---|
| 56 |
:different-colors) |
|---|
| 57 |
(,+gd-cmp-size-x+ |
|---|
| 58 |
:different-widths) |
|---|
| 59 |
(,+gd-cmp-size-y+ |
|---|
| 60 |
:different-heights) |
|---|
| 61 |
(,+gd-cmp-transparent+ |
|---|
| 62 |
:different-transparent-colors) |
|---|
| 63 |
(,+gd-cmp-background+ |
|---|
| 64 |
:different-background-colors) |
|---|
| 65 |
(,+gd-cmp-interlace+ |
|---|
| 66 |
:different-interlace-settings) |
|---|
| 67 |
(,+gd-cmp-true-color+ |
|---|
| 68 |
:true-color-versus-palette-based)) |
|---|
| 69 |
when (plusp (logand gd-flag result)) |
|---|
| 70 |
collect keyword))))) |
|---|
| 71 |
|
|---|
| 72 |
(defun copy-image (source destination |
|---|
| 73 |
source-x source-y |
|---|
| 74 |
dest-x dest-y |
|---|
| 75 |
width height |
|---|
| 76 |
&key resample |
|---|
| 77 |
rotate angle |
|---|
| 78 |
resize dest-width dest-height |
|---|
| 79 |
merge merge-gray) |
|---|
| 80 |
"Copies \(a part of) image SOURCE into image DESTINATION. Copies the |
|---|
| 81 |
rectangle with the upper left corner \(SOURCE-X,SOURCE-Y) and size |
|---|
| 82 |
WIDTH x HEIGHT to the rectangle with the upper left corner |
|---|
| 83 |
\(DEST-X,DEST-Y). |
|---|
| 84 |
|
|---|
| 85 |
If RESAMPLE is true pixel colors will be smoothly interpolated. If |
|---|
| 86 |
RESIZE is true the copied rectangle will be strechted or shrinked so |
|---|
| 87 |
that its size is DEST-WIDTH x DEST-HEIGHT. If ROTATE is true the image |
|---|
| 88 |
will be rotated by ANGLE. In this particular case DEST-X and DEST-Y |
|---|
| 89 |
specify the CENTER of the copied image rather than its upper left |
|---|
| 90 |
corner! If MERGE is true it has to be an integer in the range 0-100 |
|---|
| 91 |
and the two images will be 'merged' by the amount specified. If MERGE |
|---|
| 92 |
is 100 then the source image will simply be copied. If instead |
|---|
| 93 |
MERGE-GRAY is true the hue of the source image is preserved by |
|---|
| 94 |
converting the destination area to gray pixels before merging. |
|---|
| 95 |
|
|---|
| 96 |
The keyword options RESAMPLE, ROTATE, RESIZE, MERGE, and MERGE-GRAY |
|---|
| 97 |
are mutually exclusive \(with the exception of RESAMPLE and |
|---|
| 98 |
RESIZE). ANGLE is assumed to be specified in degrees if it's an |
|---|
| 99 |
integer, and in radians otherwise." |
|---|
| 100 |
(check-type source image) |
|---|
| 101 |
(check-type destination image) |
|---|
| 102 |
(check-type source-x integer) |
|---|
| 103 |
(check-type source-y integer) |
|---|
| 104 |
(unless rotate |
|---|
| 105 |
(check-type dest-x integer) |
|---|
| 106 |
(check-type dest-y integer)) |
|---|
| 107 |
(check-type width integer) |
|---|
| 108 |
(check-type height integer) |
|---|
| 109 |
(check-type angle (or null number)) |
|---|
| 110 |
(check-type dest-width (or null integer)) |
|---|
| 111 |
(check-type dest-height (or null integer)) |
|---|
| 112 |
(check-type merge (or null (integer 0 100))) |
|---|
| 113 |
(check-type merge-gray (or null (integer 0 100))) |
|---|
| 114 |
(when (and merge merge-gray) |
|---|
| 115 |
(error "You can't specify MERGE and MERGE-GRAY at the same time.")) |
|---|
| 116 |
(when (and (or merge merge-gray) |
|---|
| 117 |
(or resample rotate resize)) |
|---|
| 118 |
(error "MERGE and MERGE-GRAY can't be combined with RESAMPLE, ROTATE, or RESIZE.")) |
|---|
| 119 |
(when (and (or dest-width dest-height) |
|---|
| 120 |
(not resize)) |
|---|
| 121 |
(error "Use RESIZE if you want to specify DEST-WIDTH or DEST-HEIGHT")) |
|---|
| 122 |
(when (and resize |
|---|
| 123 |
(not (or dest-width dest-height))) |
|---|
| 124 |
(error "Please specify DEST-WIDTH and DEST-HEIGHT together with RESIZE.")) |
|---|
| 125 |
(when (and angle |
|---|
| 126 |
(not rotate)) |
|---|
| 127 |
(error "Use ROTATE if you want to specify ANGLE.")) |
|---|
| 128 |
(when (and rotate |
|---|
| 129 |
(not angle)) |
|---|
| 130 |
(error "Please specify ANGLE together with ROTATE.")) |
|---|
| 131 |
(when (and rotate |
|---|
| 132 |
(or resample resize)) |
|---|
| 133 |
(error "ROTATE can't be used together with RESAMPLE or RESIZE.")) |
|---|
| 134 |
(cond ((and resample resize) |
|---|
| 135 |
(gd-image-copy-resampled (img destination) (img source) |
|---|
| 136 |
dest-x dest-y source-x source-y |
|---|
| 137 |
dest-width dest-height width height)) |
|---|
| 138 |
(resample |
|---|
| 139 |
(gd-image-copy-resampled (img destination) (img source) |
|---|
| 140 |
dest-x dest-y source-x source-y |
|---|
| 141 |
width height width height)) |
|---|
| 142 |
((and rotate (integerp angle)) |
|---|
| 143 |
(gd-image-copy-rotated (img destination) (img source) |
|---|
| 144 |
(coerce dest-x 'double-float) |
|---|
| 145 |
(coerce dest-y 'double-float) |
|---|
| 146 |
source-x source-y width height angle)) |
|---|
| 147 |
(rotate |
|---|
| 148 |
(gd-image-copy-rotated (img destination) (img source) |
|---|
| 149 |
(coerce dest-x 'double-float) |
|---|
| 150 |
(coerce dest-y 'double-float) |
|---|
| 151 |
source-x source-y width height |
|---|
| 152 |
(round (* angle +radians-to-degree-factor+)))) |
|---|
| 153 |
(resize |
|---|
| 154 |
(gd-image-copy-resized (img destination) (img source) |
|---|
| 155 |
dest-x dest-y source-x source-y |
|---|
| 156 |
dest-width dest-height width height)) |
|---|
| 157 |
(merge |
|---|
| 158 |
(gd-image-copy-merge (img destination) (img source) |
|---|
| 159 |
dest-x dest-y source-x source-y |
|---|
| 160 |
width height merge)) |
|---|
| 161 |
(merge-gray |
|---|
| 162 |
(gd-image-copy-merge-gray (img destination) (img source) |
|---|
| 163 |
dest-x dest-y source-x source-y |
|---|
| 164 |
width height merge-gray)) |
|---|
| 165 |
(t |
|---|
| 166 |
(gd-image-copy (img destination) (img source) dest-x dest-y |
|---|
| 167 |
source-x source-y width height))) |
|---|
| 168 |
destination) |
|---|
| 169 |
|
|---|
| 170 |
(defun copy-palette (source destination) |
|---|
| 171 |
"Copies palette of image SOURCE to image DESTINATION attempting to |
|---|
| 172 |
match the colors in the target image to the colors in the source |
|---|
| 173 |
palette." |
|---|
| 174 |
(check-type source image) |
|---|
| 175 |
(check-type destination image) |
|---|
| 176 |
(gd-image-palette-copy (img destination) (img source)) |
|---|
| 177 |
destination) |
|---|
| 178 |
|
|---|
| 179 |
(defun true-color-to-palette (&key dither (colors-wanted 256) (image *default-image*)) |
|---|
| 180 |
"Converts the true color image IMAGE to a palette-based image using |
|---|
| 181 |
a high-quality two-pass quantization routine. If DITHER is true, the |
|---|
| 182 |
image will be dithered to approximate colors better, at the expense of |
|---|
| 183 |
some obvious \"speckling.\" COLORS-WANTED can be any positive integer |
|---|
| 184 |
up to 256 \(which is the default). If the original source image |
|---|
| 185 |
includes photographic information or anything that came out of a JPEG, |
|---|
| 186 |
256 is strongly recommended. 100% transparency of a single transparent |
|---|
| 187 |
color in the original true color image will be preserved. There is no |
|---|
| 188 |
other support for preservation of alpha channel or transparency in the |
|---|
| 189 |
destination image." |
|---|
| 190 |
(check-type image image) |
|---|
| 191 |
(check-type colors-wanted (integer 0 256)) |
|---|
| 192 |
(gd-image-true-color-to-palette (img image) |
|---|
| 193 |
(if dither 1 0) |
|---|
| 194 |
colors-wanted) |
|---|
| 195 |
image) |
|---|
| 196 |
|
|---|
| 197 |
(defmacro do-rows ((y-var &optional (image '*default-image*)) &body body) |
|---|
| 198 |
(with-rebinding (image) |
|---|
| 199 |
(with-unique-names (img width height true-color-p raw-pixels row x-var inner-body) |
|---|
| 200 |
`(let* ((,img (img ,image)) |
|---|
| 201 |
(,width (gd-image-get-sx ,img)) |
|---|
| 202 |
(,height (gd-image-get-sy ,img)) |
|---|
| 203 |
(,true-color-p (true-color-p ,image))) |
|---|
| 204 |
(declare (fixnum ,width ,height)) |
|---|
| 205 |
(cond (,true-color-p |
|---|
| 206 |
(let ((,raw-pixels (get-slot-value ,img 'gd-image 't-pixels))) |
|---|
| 207 |
(declare (type t-pixels-array ,raw-pixels)) |
|---|
| 208 |
(dotimes (,y-var ,height) |
|---|
| 209 |
(let ((,row (deref-array ,raw-pixels '(:array (* :int)) ,y-var))) |
|---|
| 210 |
(declare (type t-pixels-row ,row)) |
|---|
| 211 |
(macrolet ((do-pixels-in-row ((,x-var) &body ,inner-body) |
|---|
| 212 |
`(dotimes (,,x-var ,',width) |
|---|
| 213 |
(macrolet ((raw-pixel () |
|---|
| 214 |
`(deref-array ,',',row '(:array :int) ,',,x-var))) |
|---|
| 215 |
(locally |
|---|
| 216 |
,@,inner-body))))) |
|---|
| 217 |
(locally |
|---|
| 218 |
,@body)))))) |
|---|
| 219 |
(t |
|---|
| 220 |
(let ((,raw-pixels (get-slot-value ,img 'gd-image 'pixels))) |
|---|
| 221 |
(declare (type pixels-array ,raw-pixels)) |
|---|
| 222 |
(dotimes (,y-var ,height) |
|---|
| 223 |
(let ((,row (deref-array ,raw-pixels '(:array (* :unsigned-byte)) ,y-var))) |
|---|
| 224 |
(declare (type pixels-row ,row)) |
|---|
| 225 |
(macrolet ((do-pixels-in-row ((,x-var) &body ,inner-body) |
|---|
| 226 |
`(dotimes (,,x-var ,',width) |
|---|
| 227 |
(macrolet ((raw-pixel () |
|---|
| 228 |
`(deref-array ,',',row '(:array :unsigned-byte) ,',,x-var))) |
|---|
| 229 |
(locally |
|---|
| 230 |
,@,inner-body))))) |
|---|
| 231 |
(locally |
|---|
| 232 |
,@body))))))))))) |
|---|
| 233 |
|
|---|
| 234 |
(defmacro do-pixels ((&optional (image '*default-image*)) &body body) |
|---|
| 235 |
(with-unique-names (x y) |
|---|
| 236 |
`(do-rows (,y ,image) |
|---|
| 237 |
(do-pixels-in-row (,x) |
|---|
| 238 |
,@body)))) |
|---|