From f8e7c8cde1bde9dfac0a8245ba9b6e6d37e9d7b0 Mon Sep 17 00:00:00 2001 From: sostler Date: Wed, 14 Apr 2010 09:15:08 -0400 Subject: Rewrote upload validation --- src/site.clj | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/site.clj b/src/site.clj index ca8bc9d..bd385c3 100644 --- a/src/site.clj +++ b/src/site.clj @@ -886,15 +886,16 @@ (let [room-key (params :room) nick (session :nick) user-id (session :user_id) + image (params :image) mute ((poll *active-mutes*) user-id) - image (params :image)] + has-access (validate-room-access room-key session)] ; --TODO-- ; Because ajaxupload.js doesn't feature an error-handler, ; all responses not equal to "OK" signal errors. - (cond (not nick) (resp-success "NOT_LOGGED_IN") - (not image) (resp-success "INVALID_REQUEST") - mute (resp-success (format-mute mute)) - (not (validate-room-access room-key session)) [200 "UNKNOWN_ROOM"] + (cond (not nick) (resp-success "NOT_LOGGED_IN") + (not image) (resp-success "INVALID_REQUEST") + mute (resp-success (format-mute mute)) + (not has-access) (resp-success "UNKNOWN_ROOM") :else (do-upload session image (lookup-room room-key))))) ;; N.B. -- Upload responses aren't JSON-evaluated -- cgit v1.2.3-70-g09d2 From f0ab9c28ec3cdc0f347081e63e2448750d073d46 Mon Sep 17 00:00:00 2001 From: sostler Date: Wed, 14 Apr 2010 18:52:24 -0400 Subject: Added muting emails --- src/admin.clj | 30 ++++++++++++++++++------------ src/email.clj | 17 +++++++++++++++-- src/utils.clj | 5 ++++- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/admin.clj b/src/admin.clj index 0253734..e5d0c8f 100644 --- a/src/admin.clj +++ b/src/admin.clj @@ -50,10 +50,11 @@ (def *mute-refresh-period-sec* 60) (def fetch-mutes-query " -SELECT mutes.*, (set_on + duration) AS expiry -FROM mutes -WHERE (set_on + duration) > now() -AND NOT is_canceled +SELECT m.*, (m.set_on + m.duration) AS expiry, u.nick AS admin_nick +FROM mutes m, users u +WHERE (m.set_on + m.duration) > now() +AND u.user_id = m.admin_id +AND NOT m.is_canceled ") (defn update-mutes [] @@ -78,18 +79,23 @@ AND NOT is_canceled (defn mute! [session params] (if-vip - (let [nick (params :nick) - user-id (:user_id (fetch-nick nick)) - duration (parse-pos-interval (params :time) (s/butlast (params :unit) 1)) - reason (params :reason) - admin-id (session :user_id)] + (let [nick (params :nick) + user-id (:user_id (fetch-nick nick)) + time (params :time) + unit (params :unit) + duration (parse-pos-interval time (s/butlast unit 1)) + reason (params :reason) + admin-id (session :user_id) + admin-nick (session :nick)] (cond (not user-id) [400 "INVALID_NICK"] (not duration) [400 "INVALID_DURATION"] ;; TODO: Ugly interval hack, w/ no escaping. Totally unsafe. - :else (let [q (format "INSERT INTO mutes (user_id, admin_id, duration, reason) + :else (let [q (format "INSERT INTO mutes (user_id, admin_id, duration, reason) VALUES (%s, %s, '%s', '%s')" - user-id admin-id duration reason)] - (and (do-cmds q) "OK")))))) + user-id admin-id duration reason)] + (do-cmds q) + (send-mute-email nick admin-nick reason time unit) + "OK"))))) (defn format-mute [mute] diff --git a/src/email.clj b/src/email.clj index dbe60ea..74d6625 100644 --- a/src/email.clj +++ b/src/email.clj @@ -1,6 +1,7 @@ (ns email - (:import org.antlr.stringtemplate.StringTemplateGroup) - (:require [clojure.contrib.str-utils2 :as s])) + (:import org.antlr.stringtemplate.StringTemplateGroup) + (:require [clojure.contrib.str-utils2 :as s]) + (:use utils)) (defn base-mail [& m] (let [mail (apply hash-map m) @@ -65,6 +66,11 @@ :text text :mime (classify-mimetype text))) +(def admins ["opuscule@gmail.com" + "sbostler@gmail.com" + "stfn6000@gmail.com" + "theryderproject@gmail.com"]) + (defn send-registration-email ([nick email] (send-registration-email nick email "welcome")) ([nick email temp] @@ -75,3 +81,10 @@ (let [[s b] (parse-mail-template "reset" {"nick" nick "key" key})] (dump-mail [email] s b))) +(defn send-mute-email [user-nick admin-nick reason time unit] + (let [subject (format "%s was muted by %s for %s %s" + user-nick admin-nick time unit) + body (format "Reason: %s" + reason) + recips (join admins ",")] + (dump-mail [recips] subject body))) diff --git a/src/utils.clj b/src/utils.clj index da4d4be..d6a95e5 100755 --- a/src/utils.clj +++ b/src/utils.clj @@ -29,6 +29,9 @@ (defn swap [f] (fn [& more] (apply f (reverse more)))) +(defn join [lst int] + (apply str (interpose int lst))) + (def YYYYMMDD-format (new SimpleDateFormat "yyyyMMdd")) (defn today [] @@ -160,6 +163,6 @@ (session :is_admin)) (defmacro if-vip [e] - "Evaluates expr if user is vipm otherwise returns 404 string. Can only be used + "Evaluates expr if user is vip otherwise returns 404. Can only be used where session is defined." `(if (is-vip? ~'session) ~e (unknown-page))) -- cgit v1.2.3-70-g09d2 From ff63970f9d785672f146d82cf1da0935db4b9b5e Mon Sep 17 00:00:00 2001 From: tim b Date: Fri, 16 Apr 2010 03:33:00 -0700 Subject: send url loading result as text --- static/webcam/webcam.swf | Bin 18933 -> 18561 bytes webcam/Webcam.as | 2 +- webcam/webcam.swf | Bin 18552 -> 18524 bytes 3 files changed, 1 insertion(+), 1 deletion(-) diff --git a/static/webcam/webcam.swf b/static/webcam/webcam.swf index ce7b114..0f5882d 100644 Binary files a/static/webcam/webcam.swf and b/static/webcam/webcam.swf differ diff --git a/webcam/Webcam.as b/webcam/Webcam.as index 2077672..530e677 100644 --- a/webcam/Webcam.as +++ b/webcam/Webcam.as @@ -1 +1 @@ -package { /* JPEGCam v1.0.8 */ /* Webcam library for capturing JPEG images and submitting to a server */ /* Copyright (c) 2008 - 2009 Joseph Huckaby */ /* Licensed under the GNU Lesser Public License */ /* http://www.gnu.org/licenses/lgpl.html */ import flash.display.*; import flash.events.*; import flash.utils.*; import flash.media.Camera; import flash.media.Video; import flash.external.ExternalInterface; import flash.net.*; import flash.system.Security; import flash.system.SecurityPanel; import flash.media.Sound; import flash.media.SoundChannel; import flash.geom.Matrix; import com.adobe.images.JPGEncoder; import com.marstonstudio.UploadPostHelper; import com.neave.media.*; import com.neave.webcam.*; import com.neave.webcam.effects.* [SWF(width="320", height="240", frameRate="31", backgroundColor="0x000000")] public class Webcam extends Sprite { private var video:Video; private var webcam:NeaveWebcam; private var encoder:JPGEncoder; private var snd:Sound; private var channel:SoundChannel = new SoundChannel(); private var jpeg_quality:int; private var video_width:int; private var video_height:int; private var server_width:int; private var server_height:int; private var camera:Camera; private var bmp:Bitmap; private var bmpdata:BitmapData; private var url:String; private var form_data:Object; public function Webcam() { // class constructor flash.system.Security.allowDomain("*"); var flashvars:Object = LoaderInfo(this.root.loaderInfo).parameters; video_width = Math.floor( flashvars.width ); video_height = Math.floor( flashvars.height ); server_width = Math.floor( flashvars.server_width ); server_height = Math.floor( flashvars.server_height ); form_data = {}; stage.scaleMode = StageScaleMode.NO_SCALE; // stage.scaleMode = StageScaleMode.EXACT_FIT; stage.align = StageAlign.TOP_LEFT; stage.stageWidth = Math.max(video_width, server_width); stage.stageHeight = Math.max(video_height, server_height); // Hack to auto-select iSight camera on Mac (JPEGCam Issue #5, submitted by manuel.gonzalez.noriega) // From: http://www.squidder.com/2009/03/09/trick-auto-select-mac-isight-in-flash/ var cameraIdx:int = -1; for (var idx = 0, len = Camera.names.length; idx < len; idx++) { if (Camera.names[idx] == "USB Video Class Video") { cameraIdx = idx; idx = len; } } if (cameraIdx > -1) camera = Camera.getCamera( String(cameraIdx) ); else camera = Camera.getCamera(); if (camera != null) { camera.addEventListener(ActivityEvent.ACTIVITY, activityHandler); video = new Video( Math.max(video_width, server_width), Math.max(video_height, server_height) ); video.attachCamera(camera); webcam = new NeaveWebcam(camera, true); addChild(webcam); if ((video_width < server_width) && (video_height < server_height)) { video.scaleX = video_width / server_width; video.scaleY = video_height / server_height; } camera.setQuality(0, 100); camera.setKeyFrameInterval(10); camera.setMode( Math.max(video_width, server_width), Math.max(video_height, server_height), 30); // do not detect motion (may help reduce CPU usage) camera.setMotionLevel( 100 ); ExternalInterface.addCallback('_snap', snap); ExternalInterface.addCallback('_configure', configure); ExternalInterface.addCallback('_upload', upload); ExternalInterface.addCallback('_reset', reset); ExternalInterface.addCallback('_set_form_data', set_form_data); if (flashvars.shutter_enabled == 1) { snd = new Sound(); snd.load( new URLRequest( flashvars.shutter_url ) ); } jpeg_quality = 90; ExternalInterface.call('webcam.flash_notify', 'flashLoadComplete', true); } else { trace("You need a camera."); ExternalInterface.call('webcam.flash_notify', "error", "No camera was detected."); } } public function set_quality(new_quality:int) { // set JPEG image quality if (new_quality < 0) new_quality = 0; if (new_quality > 100) new_quality = 100; jpeg_quality = new_quality; } public function set_form_data(new_form_data:Object) { form_data = new_form_data; } public function configure(panel:String = SecurityPanel.CAMERA) { // show configure dialog inside flash movie Security.showSettings(panel); } private function activityHandler(event:ActivityEvent):void { trace("activityHandler: " + event); } public function snap(url, new_quality, shutter) { // take snapshot from camera, and upload if URL was provided if (new_quality) set_quality(new_quality); trace("in snap(), drawing to bitmap"); if (shutter) { channel = snd.play(); setTimeout( snap2, 10, url ); } else snap2(url); } public function snap2(url) { // take snapshot, convert to jpeg, submit to server bmpdata = new BitmapData( Math.max(video_width, server_width), Math.max(video_height, server_height) ); bmpdata.draw( webcam.effectBitmap ); // draw snapshot on stage bmp = new Bitmap( bmpdata ); bmp.scaleX = -1; addChild( bmp ); // stop capturing video //video.attachCamera( null ); //removeChild( video ); // if URL was provided, upload now if (url) upload( url ); } public function upload(url) { if (bmpdata) { if ((video_width > server_width) && (video_height > server_height)) { // resize image downward before submitting var tmpdata = new BitmapData(server_width, server_height); var matrix = new Matrix(); matrix.scale( server_width / video_width, server_height / video_height ); tmpdata.draw( bmpdata, matrix, null, null, null, true ); // smoothing bmpdata = tmpdata; } // need resize trace("converting to jpeg"); var ba:ByteArray; encoder = new JPGEncoder( jpeg_quality ); ba = encoder.encode( bmpdata ); trace("jpeg length: " + ba.length); //URLRequest containing the form fields and the attached image var req : URLRequest = new URLRequest(url); req.method = URLRequestMethod.POST; req.data = UploadPostHelper.getPostData( 'dump.fm.webcam.test.jpg', ba, form_data ); req.requestHeaders.push(new URLRequestHeader('Cache-Control', 'no-cache') ); req.requestHeaders.push(new URLRequestHeader('Content-Type', 'multipart/form-data; boundary=' + UploadPostHelper.getBoundary())); //URLLoader to load the request var loader : URLLoader = new URLLoader(); loader.dataFormat = URLLoaderDataFormat.BINARY; loader.addEventListener(Event.COMPLETE, onLoaded); trace("sending post to: " + url); try { loader.load(req); } catch (error:Error) { trace("Unable to load requested document."); ExternalInterface.call('webcam.flash_notify', "error", "Unable to post data: " + error); } } else { ExternalInterface.call('webcam.flash_notify', "error", "Nothing to upload, must capture an image first."); } } public function onLoaded(evt:Event):void { // image upload complete var msg = "unknown"; if (evt && evt.target && evt.target.data) msg = evt.target.data; ExternalInterface.call('webcam.flash_notify', "success", msg); } public function reset() { // reset video after taking snapshot if (bmp) { removeChild( bmp ); bmp = null; bmpdata = null; /*video.attachCamera(camera); addChild(video);*/ } } } } \ No newline at end of file +package { /* JPEGCam v1.0.8 */ /* Webcam library for capturing JPEG images and submitting to a server */ /* Copyright (c) 2008 - 2009 Joseph Huckaby */ /* Licensed under the GNU Lesser Public License */ /* http://www.gnu.org/licenses/lgpl.html */ import flash.display.*; import flash.events.*; import flash.utils.*; import flash.media.Camera; import flash.media.Video; import flash.external.ExternalInterface; import flash.net.*; import flash.system.Security; import flash.system.SecurityPanel; import flash.media.Sound; import flash.media.SoundChannel; import flash.geom.Matrix; import com.adobe.images.JPGEncoder; import com.marstonstudio.UploadPostHelper; import com.neave.media.*; import com.neave.webcam.*; import com.neave.webcam.effects.* [SWF(width="320", height="240", frameRate="31", backgroundColor="0x000000")] public class Webcam extends Sprite { private var video:Video; private var webcam:NeaveWebcam; private var encoder:JPGEncoder; private var snd:Sound; private var channel:SoundChannel = new SoundChannel(); private var jpeg_quality:int; private var video_width:int; private var video_height:int; private var server_width:int; private var server_height:int; private var camera:Camera; private var bmp:Bitmap; private var bmpdata:BitmapData; private var url:String; private var form_data:Object; public function Webcam() { // class constructor flash.system.Security.allowDomain("*"); var flashvars:Object = LoaderInfo(this.root.loaderInfo).parameters; video_width = Math.floor( flashvars.width ); video_height = Math.floor( flashvars.height ); server_width = Math.floor( flashvars.server_width ); server_height = Math.floor( flashvars.server_height ); form_data = {}; stage.scaleMode = StageScaleMode.NO_SCALE; // stage.scaleMode = StageScaleMode.EXACT_FIT; stage.align = StageAlign.TOP_LEFT; stage.stageWidth = Math.max(video_width, server_width); stage.stageHeight = Math.max(video_height, server_height); // Hack to auto-select iSight camera on Mac (JPEGCam Issue #5, submitted by manuel.gonzalez.noriega) // From: http://www.squidder.com/2009/03/09/trick-auto-select-mac-isight-in-flash/ var cameraIdx:int = -1; for (var idx = 0, len = Camera.names.length; idx < len; idx++) { if (Camera.names[idx] == "USB Video Class Video") { cameraIdx = idx; idx = len; } } if (cameraIdx > -1) camera = Camera.getCamera( String(cameraIdx) ); else camera = Camera.getCamera(); if (camera != null) { camera.addEventListener(ActivityEvent.ACTIVITY, activityHandler); video = new Video( Math.max(video_width, server_width), Math.max(video_height, server_height) ); video.attachCamera(camera); webcam = new NeaveWebcam(camera, true); addChild(webcam); if ((video_width < server_width) && (video_height < server_height)) { video.scaleX = video_width / server_width; video.scaleY = video_height / server_height; } camera.setQuality(0, 100); camera.setKeyFrameInterval(10); camera.setMode( Math.max(video_width, server_width), Math.max(video_height, server_height), 30); // do not detect motion (may help reduce CPU usage) camera.setMotionLevel( 100 ); ExternalInterface.addCallback('_snap', snap); ExternalInterface.addCallback('_configure', configure); ExternalInterface.addCallback('_upload', upload); ExternalInterface.addCallback('_reset', reset); ExternalInterface.addCallback('_set_form_data', set_form_data); if (flashvars.shutter_enabled == 1) { snd = new Sound(); snd.load( new URLRequest( flashvars.shutter_url ) ); } jpeg_quality = 90; ExternalInterface.call('webcam.flash_notify', 'flashLoadComplete', true); } else { trace("You need a camera."); ExternalInterface.call('webcam.flash_notify', "error", "No camera was detected."); } } public function set_quality(new_quality:int) { // set JPEG image quality if (new_quality < 0) new_quality = 0; if (new_quality > 100) new_quality = 100; jpeg_quality = new_quality; } public function set_form_data(new_form_data:Object) { form_data = new_form_data; } public function configure(panel:String = SecurityPanel.CAMERA) { // show configure dialog inside flash movie Security.showSettings(panel); } private function activityHandler(event:ActivityEvent):void { trace("activityHandler: " + event); } public function snap(url, new_quality, shutter) { // take snapshot from camera, and upload if URL was provided if (new_quality) set_quality(new_quality); trace("in snap(), drawing to bitmap"); if (shutter) { channel = snd.play(); setTimeout( snap2, 10, url ); } else snap2(url); } public function snap2(url) { // take snapshot, convert to jpeg, submit to server bmpdata = new BitmapData( Math.max(video_width, server_width), Math.max(video_height, server_height) ); bmpdata.draw( webcam.effectBitmap ); // draw snapshot on stage bmp = new Bitmap( bmpdata ); bmp.scaleX = -1; addChild( bmp ); // stop capturing video //video.attachCamera( null ); //removeChild( video ); // if URL was provided, upload now if (url) upload( url ); } public function upload(url) { if (bmpdata) { if ((video_width > server_width) && (video_height > server_height)) { // resize image downward before submitting var tmpdata = new BitmapData(server_width, server_height); var matrix = new Matrix(); matrix.scale( server_width / video_width, server_height / video_height ); tmpdata.draw( bmpdata, matrix, null, null, null, true ); // smoothing bmpdata = tmpdata; } // need resize trace("converting to jpeg"); var ba:ByteArray; encoder = new JPGEncoder( jpeg_quality ); ba = encoder.encode( bmpdata ); trace("jpeg length: " + ba.length); //URLRequest containing the form fields and the attached image var req : URLRequest = new URLRequest(url); req.method = URLRequestMethod.POST; req.data = UploadPostHelper.getPostData( 'dump.fm.webcam.test.jpg', ba, form_data ); req.requestHeaders.push(new URLRequestHeader('Cache-Control', 'no-cache') ); req.requestHeaders.push(new URLRequestHeader('Content-Type', 'multipart/form-data; boundary=' + UploadPostHelper.getBoundary())); //URLLoader to load the request var loader : URLLoader = new URLLoader(); loader.dataFormat = URLLoaderDataFormat.TEXT; loader.addEventListener(Event.COMPLETE, onLoaded); trace("sending post to: " + url); try { loader.load(req); } catch (error:Error) { trace("Unable to load requested document."); ExternalInterface.call('webcam.flash_notify', "error", "Unable to post data: " + error); } } else { ExternalInterface.call('webcam.flash_notify', "error", "Nothing to upload, must capture an image first."); } } public function onLoaded(evt:Event):void { // image upload complete var msg = "unknown"; if (evt && evt.target && evt.target.data) msg = evt.target.data; ExternalInterface.call('webcam.flash_notify', "success", msg); } public function reset() { // reset video after taking snapshot if (bmp) { removeChild( bmp ); bmp = null; bmpdata = null; /*video.attachCamera(camera); addChild(video);*/ } } } } \ No newline at end of file diff --git a/webcam/webcam.swf b/webcam/webcam.swf index 7999270..4ea9f45 100644 Binary files a/webcam/webcam.swf and b/webcam/webcam.swf differ -- cgit v1.2.3-70-g09d2 From 73130a732df811e55a96ba38c11c0cb0f6090a8e Mon Sep 17 00:00:00 2001 From: timb Date: Mon, 19 Apr 2010 08:52:36 -0700 Subject: graphicsmagick patch --- doc/gm.patch | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 doc/gm.patch diff --git a/doc/gm.patch b/doc/gm.patch new file mode 100644 index 0000000..a82f9b4 --- /dev/null +++ b/doc/gm.patch @@ -0,0 +1,140 @@ +diff -ur gm.old/coders/gif.c gm.new/coders/gif.c +--- gm.old/coders/gif.c 2009-12-16 10:50:03.000000000 -0800 ++++ gm.new/coders/gif.c 2010-04-19 08:07:34.000000000 -0700 +@@ -1022,6 +1022,8 @@ + image->colormap[i].red=ScaleCharToQuantum(*p++); + image->colormap[i].green=ScaleCharToQuantum(*p++); + image->colormap[i].blue=ScaleCharToQuantum(*p++); ++ if (i == opacity) ++ image->colormap[i].opacity=(Quantum) TransparentOpacity; + } + image->background_color= + image->colormap[Min(background,image->colors-1)]; +@@ -1048,6 +1050,8 @@ + image->colormap[i].red=ScaleCharToQuantum(*p++); + image->colormap[i].green=ScaleCharToQuantum(*p++); + image->colormap[i].blue=ScaleCharToQuantum(*p++); ++ if (i == opacity) ++ image->colormap[i].opacity=(Quantum) TransparentOpacity; + } + MagickFreeMemory(colormap); + } +diff -ur gm.old/magick/image.c gm.new/magick/image.c +--- gm.old/magick/image.c 2010-03-02 13:46:44.000000000 -0800 ++++ gm.new/magick/image.c 2010-04-19 08:20:33.000000000 -0700 +@@ -1925,7 +1925,57 @@ + image->is_monochrome=IsMonochrome(image->background_color); + return status; + } +- ++ ++/* ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++% % ++% % ++% S e t I m a g e C o l o r % ++% % ++% % ++% % ++%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ++% ++% SetImageColor() sets the red, green, blue and opacity components of each pixel to ++% a specified level. ++% ++% The format of the SetImageColor method is: ++% ++% void SetImageColor(Image *image,const PixelPacket pixel) ++% ++% A description of each parameter follows: ++% ++% o image: The image. ++% ++% o pixel: Set each pixel in the image to this pixel's color and transparency. ++% ++% ++*/ ++MagickExport MagickPassFail SetImageColor(Image *image, const PixelPacket pixel) ++{ ++ MagickPassFail ++ status; ++ ++ assert(image != (Image *) NULL); ++ assert(image->signature == MagickSignature); ++ status=MagickPass; ++ ++ image->matte=True; ++ image->colorspace=RGBColorspace; ++ image->storage_class=DirectClass; ++ ++ status=PixelIterateMonoModify(SetImageColorCallBack,NULL, ++ SetImageColorText, ++ NULL,&pixel,0,0, ++ image->columns,image->rows, ++ image,&image->exception); ++ ++ image->is_grayscale=IsGray(image->background_color); ++ image->is_monochrome=IsMonochrome(image->background_color); ++ return status; ++} ++ ++ + /* + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % % +diff -ur gm.old/magick/image.h gm.new/magick/image.h +--- gm.old/magick/image.h 2010-03-01 13:15:28.000000000 -0800 ++++ gm.new/magick/image.h 2010-04-19 07:27:27.000000000 -0700 +@@ -1008,6 +1008,7 @@ + DisplayImages(const ImageInfo *image_info,Image *image), + RemoveDefinitions(const ImageInfo *image_info,const char *options), + SetImage(Image *,const Quantum), ++ SetImageColor(Image *,const PixelPacket), + SetImageClipMask(Image *image,const Image *clip_mask), + SetImageDepth(Image *,const unsigned long), + SetImageInfo(ImageInfo *image_info,const unsigned int flags,ExceptionInfo *exception), +diff -ur gm.old/magick/transform.c gm.new/magick/transform.c +--- gm.old/magick/transform.c 2010-02-27 13:30:13.000000000 -0800 ++++ gm.new/magick/transform.c 2010-04-19 07:28:35.000000000 -0700 +@@ -317,6 +317,12 @@ + register const Image + *next; + ++ register long ++ i; ++ ++ MagickBool ++ found_transparency=False; ++ + /* + Coalesce the image sequence. + */ +@@ -352,9 +358,19 @@ + } + case BackgroundDispose: + { ++ /* fill image with transparent color, if one exists */ + coalesce_image->next=CloneImage(coalesce_image,0,0,True,exception); +- if (coalesce_image->next != (Image *) NULL) +- (void) SetImage(coalesce_image->next,OpaqueOpacity); ++ if (coalesce_image->next != (Image *) NULL) { ++ for (i = 0; i < (long) coalesce_image->colors; i++) { ++ if (coalesce_image->colormap[i].opacity == TransparentOpacity) { ++ found_transparency = True; ++ (void) SetImageColor(coalesce_image->next,coalesce_image->colormap[i]); ++ break; ++ } ++ } ++ if (!found_transparency) ++ (void) SetImage(coalesce_image->next,OpaqueOpacity); ++ } + break; + } + case PreviousDispose: +@@ -376,6 +392,7 @@ + (void) CompositeImage(coalesce_image,next->matte ? OverCompositeOp : + CopyCompositeOp,next,next->page.x,next->page.y); + } ++ + while (coalesce_image->previous != (Image *) NULL) + coalesce_image=coalesce_image->previous; + return(coalesce_image); -- cgit v1.2.3-70-g09d2