<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use devtools_traits::TimelineMarkerType;
use devtools_traits::{AutoMargins, CONSOLE_API, CachedConsoleMessage, CachedConsoleMessageTypes};
use devtools_traits::{ComputedNodeLayout, ConsoleAPI, PageError, ScriptToDevtoolsControlMsg};
use devtools_traits::{EvaluateJSReply, Modification, NodeInfo, PAGE_ERROR, TimelineMarker};
use dom::bindings::codegen::Bindings::CSSStyleDeclarationBinding::CSSStyleDeclarationMethods;
use dom::bindings::codegen::Bindings::DOMRectBinding::DOMRectMethods;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::conversions::{FromJSValConvertible, jsstring_to_str};
use dom::bindings::global::GlobalRef;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::Root;
use dom::bindings::str::DOMString;
use dom::browsingcontext::BrowsingContext;
use dom::element::Element;
use dom::node::Node;
use dom::window::Window;
use ipc_channel::ipc::IpcSender;
use js::jsapi::{ObjectClassName, RootedObject, RootedValue};
use js::jsval::UndefinedValue;
use msg::constellation_msg::PipelineId;
use script_thread::get_browsing_context;
use std::ffi::CStr;
use std::str;
use style::properties::longhands::{margin_top, margin_right, margin_bottom, margin_left};
use uuid::Uuid;

#[allow(unsafe_code)]
pub fn handle_evaluate_js(global: &GlobalRef, eval: String, reply: IpcSender<EvaluateJSReply>) {
    // global.get_cx() returns a valid `JSContext` pointer, so this is safe.
    let result = unsafe {
        let cx = global.get_cx();
        let mut rval = RootedValue::new(cx, UndefinedValue());
        global.evaluate_js_on_global_with_result(&eval, rval.handle_mut());

        if rval.ptr.is_undefined() {
            EvaluateJSReply::VoidValue
        } else if rval.ptr.is_boolean() {
            EvaluateJSReply::BooleanValue(rval.ptr.to_boolean())
        } else if rval.ptr.is_double() || rval.ptr.is_int32() {
            EvaluateJSReply::NumberValue(FromJSValConvertible::from_jsval(cx, rval.handle(), ())
                                             .unwrap())
        } else if rval.ptr.is_string() {
            EvaluateJSReply::StringValue(String::from(jsstring_to_str(cx, rval.ptr.to_string())))
        } else if rval.ptr.is_null() {
            EvaluateJSReply::NullValue
        } else {
            assert!(rval.ptr.is_object());

            let obj = RootedObject::new(cx, rval.ptr.to_object());
            let class_name = CStr::from_ptr(ObjectClassName(cx, obj.handle()));
            let class_name = str::from_utf8(class_name.to_bytes()).unwrap();

            EvaluateJSReply::ActorValue {
                class: class_name.to_owned(),
                uuid: Uuid::new_v4().to_string(),
            }
        }
    };
    reply.send(result).unwrap();
}

pub fn handle_get_root_node(context: &BrowsingContext, pipeline: PipelineId, reply: IpcSender<NodeInfo>) {
    let context = get_browsing_context(context, pipeline);
    let document = context.active_document();

    let node = document.upcast::<Node>();
    reply.send(node.summarize()).unwrap();
}

pub fn handle_get_document_element(context: &BrowsingContext,
                                   pipeline: PipelineId,
                                   reply: IpcSender<NodeInfo>) {
    let context = get_browsing_context(context, pipeline);
    let document = context.active_document();
    let document_element = document.GetDocumentElement().unwrap();

    let node = document_element.upcast::<Node>();
    reply.send(node.summarize()).unwrap();
}

fn find_node_by_unique_id(context: &BrowsingContext,
                          pipeline: PipelineId,
                          node_id: String)
                          -> Root<Node> {
    let context = get_browsing_context(context, pipeline);
    let document = context.active_document();
    let node = document.upcast::<Node>();

    for candidate in node.traverse_preorder() {
        if candidate.unique_id() == node_id {
            return candidate;
        }
    }

    panic!("couldn't find node with unique id {}", node_id)
}

pub fn handle_get_children(context: &BrowsingContext,
                           pipeline: PipelineId,
                           node_id: String,
                           reply: IpcSender<Vec<NodeInfo>>) {
    let parent = find_node_by_unique_id(context, pipeline, node_id);
    let children = parent.children()
                         .map(|child| child.summarize())
                         .collect();
    reply.send(children).unwrap();
}

pub fn handle_get_layout(context: &BrowsingContext,
                         pipeline: PipelineId,
                         node_id: String,
                         reply: IpcSender<ComputedNodeLayout>) {
    let node = find_node_by_unique_id(context, pipeline, node_id);

    let elem = node.downcast::<Element>().expect("should be getting layout of element");
    let rect = elem.GetBoundingClientRect();
    let width = rect.Width() as f32;
    let height = rect.Height() as f32;

    let window = context.active_window();
    let elem = node.downcast::<Element>().expect("should be getting layout of element");
    let computed_style = window.r().GetComputedStyle(elem, None);

    reply.send(ComputedNodeLayout {
        display: String::from(computed_style.Display()),
        position: String::from(computed_style.Position()),
        zIndex: String::from(computed_style.ZIndex()),
        boxSizing: String::from(computed_style.BoxSizing()),
        autoMargins: determine_auto_margins(&window, &*node),
        marginTop: String::from(computed_style.MarginTop()),
        marginRight: String::from(computed_style.MarginRight()),
        marginBottom: String::from(computed_style.MarginBottom()),
        marginLeft: String::from(computed_style.MarginLeft()),
        borderTopWidth: String::from(computed_style.BorderTopWidth()),
        borderRightWidth: String::from(computed_style.BorderRightWidth()),
        borderBottomWidth: String::from(computed_style.BorderBottomWidth()),
        borderLeftWidth: String::from(computed_style.BorderLeftWidth()),
        paddingTop: String::from(computed_style.PaddingTop()),
        paddingRight: String::from(computed_style.PaddingRight()),
        paddingBottom: String::from(computed_style.PaddingBottom()),
        paddingLeft: String::from(computed_style.PaddingLeft()),
        width: width,
        height: height,
    }).unwrap();
}

fn determine_auto_margins(window: &Window, node: &Node) -> AutoMargins {
    let margin = window.margin_style_query(node.to_trusted_node_address());
    AutoMargins {
        top: margin.top == margin_top::computed_value::T::Auto,
        right: margin.right == margin_right::computed_value::T::Auto,
        bottom: margin.bottom == margin_bottom::computed_value::T::Auto,
        left: margin.left == margin_left::computed_value::T::Auto,
    }
}

pub fn handle_get_cached_messages(_pipeline_id: PipelineId,
                                  message_types: CachedConsoleMessageTypes,
                                  reply: IpcSender<Vec<CachedConsoleMessage>>) {
    // TODO: check the messageTypes against a global Cache for console messages and page exceptions
    let mut messages = Vec::new();
    if message_types.contains(PAGE_ERROR) {
        // TODO: make script error reporter pass all reported errors
        //      to devtools and cache them for returning here.
        let msg = PageError {
            type_: "PageError".to_owned(),
            errorMessage: "page error test".to_owned(),
            sourceName: String::new(),
            lineText: String::new(),
            lineNumber: 0,
            columnNumber: 0,
            category: String::new(),
            timeStamp: 0,
            error: false,
            warning: false,
            exception: false,
            strict: false,
            private: false,
        };
        messages.push(CachedConsoleMessage::PageError(msg));
    }
    if message_types.contains(CONSOLE_API) {
        // TODO: do for real
        let msg = ConsoleAPI {
            type_: "ConsoleAPI".to_owned(),
            level: "error".to_owned(),
            filename: "http://localhost/~mihai/mozilla/test.html".to_owned(),
            lineNumber: 0,
            functionName: String::new(),
            timeStamp: 0,
            private: false,
            arguments: vec!["console error test".to_owned()],
        };
        messages.push(CachedConsoleMessage::ConsoleAPI(msg));
    }
    reply.send(messages).unwrap();
}

pub fn handle_modify_attribute(context: &BrowsingContext,
                               pipeline: PipelineId,
                               node_id: String,
                               modifications: Vec<Modification>) {
    let node = find_node_by_unique_id(context, pipeline, node_id);
    let elem = node.downcast::<Element>().expect("should be getting layout of element");

    for modification in modifications {
        match modification.newValue {
            Some(string) => {
                let _ = elem.SetAttribute(DOMString::from(modification.attributeName),
                                          DOMString::from(string));
            },
            None => elem.RemoveAttribute(DOMString::from(modification.attributeName)),
        }
    }
}

pub fn handle_wants_live_notifications(global: &GlobalRef, send_notifications: bool) {
    global.set_devtools_wants_updates(send_notifications);
}

pub fn handle_set_timeline_markers(context: &BrowsingContext,
                                   marker_types: Vec<TimelineMarkerType>,
                                   reply: IpcSender<TimelineMarker>) {
    let window = context.active_window();
    window.set_devtools_timeline_markers(marker_types, reply);
}

pub fn handle_drop_timeline_markers(context: &BrowsingContext,
                                    marker_types: Vec<TimelineMarkerType>) {
    let window = context.active_window();
    window.drop_devtools_timeline_markers(marker_types);
}

pub fn handle_request_animation_frame(context: &BrowsingContext,
                                      id: PipelineId,
                                      actor_name: String) {
    let context = context.find(id).expect("There is no such context");
    let doc = context.active_document();
    let devtools_sender = context.active_window().devtools_chan().unwrap();
    doc.request_animation_frame(box move |time| {
        let msg = ScriptToDevtoolsControlMsg::FramerateTick(actor_name, time);
        devtools_sender.send(msg).unwrap();
    });
}
<commit_msg>Use JSautoCompartment in devtools handle_evaluate_js to avoid segfault<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use devtools_traits::TimelineMarkerType;
use devtools_traits::{AutoMargins, CONSOLE_API, CachedConsoleMessage, CachedConsoleMessageTypes};
use devtools_traits::{ComputedNodeLayout, ConsoleAPI, PageError, ScriptToDevtoolsControlMsg};
use devtools_traits::{EvaluateJSReply, Modification, NodeInfo, PAGE_ERROR, TimelineMarker};
use dom::bindings::codegen::Bindings::CSSStyleDeclarationBinding::CSSStyleDeclarationMethods;
use dom::bindings::codegen::Bindings::DOMRectBinding::DOMRectMethods;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::conversions::{FromJSValConvertible, jsstring_to_str};
use dom::bindings::global::GlobalRef;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::Root;
use dom::bindings::str::DOMString;
use dom::browsingcontext::BrowsingContext;
use dom::element::Element;
use dom::node::Node;
use dom::window::Window;
use ipc_channel::ipc::IpcSender;
use js::jsapi::{JSAutoCompartment, ObjectClassName, RootedObject, RootedValue};
use js::jsval::UndefinedValue;
use msg::constellation_msg::PipelineId;
use script_thread::get_browsing_context;
use std::ffi::CStr;
use std::str;
use style::properties::longhands::{margin_top, margin_right, margin_bottom, margin_left};
use uuid::Uuid;

#[allow(unsafe_code)]
pub fn handle_evaluate_js(global: &GlobalRef, eval: String, reply: IpcSender<EvaluateJSReply>) {
    // global.get_cx() returns a valid `JSContext` pointer, so this is safe.
    let result = unsafe {
        let cx = global.get_cx();
        let globalhandle = global.reflector().get_jsobject();
        let _ac = JSAutoCompartment::new(cx, globalhandle.get());
        let mut rval = RootedValue::new(cx, UndefinedValue());
        global.evaluate_js_on_global_with_result(&eval, rval.handle_mut());

        if rval.ptr.is_undefined() {
            EvaluateJSReply::VoidValue
        } else if rval.ptr.is_boolean() {
            EvaluateJSReply::BooleanValue(rval.ptr.to_boolean())
        } else if rval.ptr.is_double() || rval.ptr.is_int32() {
            EvaluateJSReply::NumberValue(FromJSValConvertible::from_jsval(cx, rval.handle(), ())
                                             .unwrap())
        } else if rval.ptr.is_string() {
            EvaluateJSReply::StringValue(String::from(jsstring_to_str(cx, rval.ptr.to_string())))
        } else if rval.ptr.is_null() {
            EvaluateJSReply::NullValue
        } else {
            assert!(rval.ptr.is_object());

            let obj = RootedObject::new(cx, rval.ptr.to_object());
            let class_name = CStr::from_ptr(ObjectClassName(cx, obj.handle()));
            let class_name = str::from_utf8(class_name.to_bytes()).unwrap();

            EvaluateJSReply::ActorValue {
                class: class_name.to_owned(),
                uuid: Uuid::new_v4().to_string(),
            }
        }
    };
    reply.send(result).unwrap();
}

pub fn handle_get_root_node(context: &BrowsingContext, pipeline: PipelineId, reply: IpcSender<NodeInfo>) {
    let context = get_browsing_context(context, pipeline);
    let document = context.active_document();

    let node = document.upcast::<Node>();
    reply.send(node.summarize()).unwrap();
}

pub fn handle_get_document_element(context: &BrowsingContext,
                                   pipeline: PipelineId,
                                   reply: IpcSender<NodeInfo>) {
    let context = get_browsing_context(context, pipeline);
    let document = context.active_document();
    let document_element = document.GetDocumentElement().unwrap();

    let node = document_element.upcast::<Node>();
    reply.send(node.summarize()).unwrap();
}

fn find_node_by_unique_id(context: &BrowsingContext,
                          pipeline: PipelineId,
                          node_id: String)
                          -> Root<Node> {
    let context = get_browsing_context(context, pipeline);
    let document = context.active_document();
    let node = document.upcast::<Node>();

    for candidate in node.traverse_preorder() {
        if candidate.unique_id() == node_id {
            return candidate;
        }
    }

    panic!("couldn't find node with unique id {}", node_id)
}

pub fn handle_get_children(context: &BrowsingContext,
                           pipeline: PipelineId,
                           node_id: String,
                           reply: IpcSender<Vec<NodeInfo>>) {
    let parent = find_node_by_unique_id(context, pipeline, node_id);
    let children = parent.children()
                         .map(|child| child.summarize())
                         .collect();
    reply.send(children).unwrap();
}

pub fn handle_get_layout(context: &BrowsingContext,
                         pipeline: PipelineId,
                         node_id: String,
                         reply: IpcSender<ComputedNodeLayout>) {
    let node = find_node_by_unique_id(context, pipeline, node_id);

    let elem = node.downcast::<Element>().expect("should be getting layout of element");
    let rect = elem.GetBoundingClientRect();
    let width = rect.Width() as f32;
    let height = rect.Height() as f32;

    let window = context.active_window();
    let elem = node.downcast::<Element>().expect("should be getting layout of element");
    let computed_style = window.r().GetComputedStyle(elem, None);

    reply.send(ComputedNodeLayout {
        display: String::from(computed_style.Display()),
        position: String::from(computed_style.Position()),
        zIndex: String::from(computed_style.ZIndex()),
        boxSizing: String::from(computed_style.BoxSizing()),
        autoMargins: determine_auto_margins(&window, &*node),
        marginTop: String::from(computed_style.MarginTop()),
        marginRight: String::from(computed_style.MarginRight()),
        marginBottom: String::from(computed_style.MarginBottom()),
        marginLeft: String::from(computed_style.MarginLeft()),
        borderTopWidth: String::from(computed_style.BorderTopWidth()),
        borderRightWidth: String::from(computed_style.BorderRightWidth()),
        borderBottomWidth: String::from(computed_style.BorderBottomWidth()),
        borderLeftWidth: String::from(computed_style.BorderLeftWidth()),
        paddingTop: String::from(computed_style.PaddingTop()),
        paddingRight: String::from(computed_style.PaddingRight()),
        paddingBottom: String::from(computed_style.PaddingBottom()),
        paddingLeft: String::from(computed_style.PaddingLeft()),
        width: width,
        height: height,
    }).unwrap();
}

fn determine_auto_margins(window: &Window, node: &Node) -> AutoMargins {
    let margin = window.margin_style_query(node.to_trusted_node_address());
    AutoMargins {
        top: margin.top == margin_top::computed_value::T::Auto,
        right: margin.right == margin_right::computed_value::T::Auto,
        bottom: margin.bottom == margin_bottom::computed_value::T::Auto,
        left: margin.left == margin_left::computed_value::T::Auto,
    }
}

pub fn handle_get_cached_messages(_pipeline_id: PipelineId,
                                  message_types: CachedConsoleMessageTypes,
                                  reply: IpcSender<Vec<CachedConsoleMessage>>) {
    // TODO: check the messageTypes against a global Cache for console messages and page exceptions
    let mut messages = Vec::new();
    if message_types.contains(PAGE_ERROR) {
        // TODO: make script error reporter pass all reported errors
        //      to devtools and cache them for returning here.
        let msg = PageError {
            type_: "PageError".to_owned(),
            errorMessage: "page error test".to_owned(),
            sourceName: String::new(),
            lineText: String::new(),
            lineNumber: 0,
            columnNumber: 0,
            category: String::new(),
            timeStamp: 0,
            error: false,
            warning: false,
            exception: false,
            strict: false,
            private: false,
        };
        messages.push(CachedConsoleMessage::PageError(msg));
    }
    if message_types.contains(CONSOLE_API) {
        // TODO: do for real
        let msg = ConsoleAPI {
            type_: "ConsoleAPI".to_owned(),
            level: "error".to_owned(),
            filename: "http://localhost/~mihai/mozilla/test.html".to_owned(),
            lineNumber: 0,
            functionName: String::new(),
            timeStamp: 0,
            private: false,
            arguments: vec!["console error test".to_owned()],
        };
        messages.push(CachedConsoleMessage::ConsoleAPI(msg));
    }
    reply.send(messages).unwrap();
}

pub fn handle_modify_attribute(context: &BrowsingContext,
                               pipeline: PipelineId,
                               node_id: String,
                               modifications: Vec<Modification>) {
    let node = find_node_by_unique_id(context, pipeline, node_id);
    let elem = node.downcast::<Element>().expect("should be getting layout of element");

    for modification in modifications {
        match modification.newValue {
            Some(string) => {
                let _ = elem.SetAttribute(DOMString::from(modification.attributeName),
                                          DOMString::from(string));
            },
            None => elem.RemoveAttribute(DOMString::from(modification.attributeName)),
        }
    }
}

pub fn handle_wants_live_notifications(global: &GlobalRef, send_notifications: bool) {
    global.set_devtools_wants_updates(send_notifications);
}

pub fn handle_set_timeline_markers(context: &BrowsingContext,
                                   marker_types: Vec<TimelineMarkerType>,
                                   reply: IpcSender<TimelineMarker>) {
    let window = context.active_window();
    window.set_devtools_timeline_markers(marker_types, reply);
}

pub fn handle_drop_timeline_markers(context: &BrowsingContext,
                                    marker_types: Vec<TimelineMarkerType>) {
    let window = context.active_window();
    window.drop_devtools_timeline_markers(marker_types);
}

pub fn handle_request_animation_frame(context: &BrowsingContext,
                                      id: PipelineId,
                                      actor_name: String) {
    let context = context.find(id).expect("There is no such context");
    let doc = context.active_document();
    let devtools_sender = context.active_window().devtools_chan().unwrap();
    doc.request_animation_frame(box move |time| {
        let msg = ScriptToDevtoolsControlMsg::FramerateTick(actor_name, time);
        devtools_sender.send(msg).unwrap();
    });
}
<|endoftext|>


<commit_before>use std::io::{Read, Error};

#[derive(Copy, Clone, Eq, PartialEq)]
enum MultiLineMark {
	None,
	Space,
	NewLine,
	Cr,
	Lf,
}

enum State {
	Code(MultiLineMark),
	Quote(u8),
	Escape(u8),
	SingleLineComment(MultiLineMark),
	MultLineComment(MultiLineMark),
}

/**
 * Filter for removing comments from preprocessed C/C++ code.
 */
pub struct CommentsRemover<R> {
	r: R,
	state: State,
	last: Option<u8>,
}

impl<R: Read> CommentsRemover<R> {
	pub fn new(r: R) -> CommentsRemover<R> {
		CommentsRemover {
			r: r,
			state: State::Code(MultiLineMark::None),
			last: None,
		}
	}
}

impl<R: Read> Read for CommentsRemover<R> {
	fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
		if buf.len() == 0 {
			return Ok(0);
		}
		let size = try! (self.r.read(buf));
		let mut offset = 0;
		for index in 0..size {
			let c = buf[index];
			match self.state {
				State::Code(multiline) => {
					match c {
						b'"' | b'\'' => {
							self.state = State::Quote(c);
						}
						b'/' if self.last == Some(b'/') => {
							self.last = None;
							self.state = State::SingleLineComment(multiline);
						}
						b'*' if self.last == Some(b'/') => {
							self.last = None;
							self.state = State::MultLineComment(multiline);
						}
						c => {
							match self.last {
								Some(last) => {
									buf[offset] = last;
									offset += 1;
								}
								None => {
								}
							}
							self.last = Some(c);
							self.state = State::Code(match c {
								b'/' => multiline,
								b'\\' => MultiLineMark::Space,
								b'\t' | b' ' if multiline == MultiLineMark::Space => MultiLineMark::Space,
								b'\t' | b' ' => MultiLineMark::NewLine,
								b'\n' if multiline == MultiLineMark::Space => MultiLineMark::Lf,
								b'\n' if multiline == MultiLineMark::Cr => MultiLineMark::Cr,
								b'\r' if multiline == MultiLineMark::Space => MultiLineMark::Cr,
								b'\r' if multiline == MultiLineMark::Lf => MultiLineMark::Lf,
								_ => MultiLineMark::None,
							});
							continue;
						}
					};
				}
				State::Escape(q) => {
					self.state = State::Quote(q);
				}
				State::Quote(q) => {
					match c {
						b'\\' => {
							self.state = State::Escape(q);
						}
						c if c == q => {
							self.state = State::Code(MultiLineMark::None);
						}
						_ => {
						}
					}
				}
				State::SingleLineComment(multiline) => {
					match c {
						b'\n' | b'\r' => {
							self.state = State::Code(multiline);
						}
						_ => {
						}
					}
				}
				State::MultLineComment(multiline) => {
					match c {
						b'/' if self.last == Some(b'*') => {
							self.last = None;
							self.state = State::Code(multiline);
							continue;
						}
						v => {
							self.last = Some(v);
						}
					}
				}
			}
			match self.state {
				State::Code(_) | State::Quote(_) | State::Escape(_) => {
					match self.last {
						Some(c) => {
							buf[offset] = c;
							offset += 1;
						}
						None => {
						}
					}
					self.last = Some(c);
				}
				State::MultLineComment(multiline) => {
					match c {
						b'\n' | b'\r' => {
							match multiline {
								MultiLineMark::NewLine => {
									self.state = State::MultLineComment(match c {
										b'\n' => MultiLineMark::Lf,
										b'\r' => MultiLineMark::Cr,
										_ => MultiLineMark::None,
									});
									buf[offset] = b'\\';
									offset += 1;
								}
								MultiLineMark::Space => {
									self.state = State::MultLineComment(match c {
										b'\n' => MultiLineMark::Lf,
										b'\r' => MultiLineMark::Cr,
										_ => MultiLineMark::None,
									});
								}
								MultiLineMark::Lf if c == b'\n' => {
									buf[offset] = b'\\';
									offset += 1;
								}
								MultiLineMark::Cr if c == b'\r' => {
									buf[offset] = b'\\';
									offset += 1;
								}
							    _ => {
							    }
							}
							buf[offset] = c;
							offset += 1;
						}
						_ => {
						}
					}
				}
				State::SingleLineComment(_) => {
				}
			}
		}
		if offset < buf.len() {
			match self.state {
				State::Code(_) | State::Quote(_) | State::Escape(_) => {
					match self.last {
						Some(c) if c == b'/' => {
						}
						Some(c) => {
							buf[offset] = c;
							offset += 1;
							self.last = None;
						}
						None => {
						}
					}
				}
				State::SingleLineComment(_) | State::MultLineComment(_) => {
				}
			}
		}
		if size == 0 {
			return Ok(offset);
		}
		match offset {
			0 => self.read(buf),
			_ => Ok(offset),
		}
	}
}

#[cfg(test)]
mod test {
	use std::io::{Read, Write, Cursor};
	use super::CommentsRemover;

	fn check_filter_pass(original: &str, expected: &str, block_size: usize) {
		let mut stream: Vec<u8> = Vec::new();
		stream.write(original.as_bytes()).unwrap();

		let mut filter = CommentsRemover::new(Cursor::new(stream));
		let mut actual = Vec::new();
		let mut buffer = vec![0; block_size];
		loop {
			let size = filter.read(&mut buffer).unwrap();
			if size == 0 {
				break;
			}
			actual.write(&buffer[0..size]).unwrap();
			assert!(actual.len() <= expected.len());
		}
		assert_eq!(expected, String::from_utf8(actual).unwrap());
	}

	fn check_filter(original: &str, expected: &str) {
		check_filter_pass(original, expected, expected.len());
		check_filter_pass(original, expected, original.len());
		check_filter_pass(original, expected, 1);
		check_filter_pass(original, expected, 10);
		check_filter_pass(original, expected, expected.len() - 1);
		check_filter_pass(original, expected, original.len() - 1);
	}

	#[test]
	fn test_filter_no_comments() {
		let source = r#"#line 1 "sample.cpp"
#line 1 "e:/work/octobuild/test_cl/sample header.h"
# pragma once
void hello();
#line 2 "sample.cpp"

int main(int argc, char **argv) {
	return 0;
}
"#;
		check_filter(source, source);
	}

	#[test]
	fn test_filter_simple_comments() {
		check_filter(
r#"// Some data
#line 1 "e:/work//octobuild/test_cl/sample /* header */.h"
# pragma once /*/ foo */
void hello();
//#line 2 "sample.cpp"

int main(int argc, char **argv /* // Arguments */) {
/*
 * Multiline
 */
	return 0;
}
"#,
r#"
#line 1 "e:/work//octobuild/test_cl/sample /* header */.h"
# pragma once
void hello();

int main(int argc, char **argv ) {

	return 0;
}
"#
		);
	}

	#[test]
	fn test_filter_define_comments() {
		check_filter(
r#"#define A "A1" \
   /* Foo
Bar */ \
          "A2"

#define B "B1" \  /* Buzz */ /*X*//* Foo
Bar */ \
          "B2"

int main() {
    return 0;
}
"#,
r#"#define A "A1" \
   \
 \
          "A2"

#define B "B1" \
 \
          "B2"

int main() {
    return 0;
}
"#
		);
	}
}
<commit_msg>Fix multiline comments with multiline defines<commit_after>use std::io::{Read, Error, ErrorKind};

#[derive(Copy, Clone, Eq, PartialEq)]
enum MultiLineMark {
	None,
	Space,
	NewLine,
	Cr,
	Lf,
}

enum State {
	Code(MultiLineMark, Option<u8>),
	Quote(u8, Option<u8>),
	Escape(u8),
	SingleLineComment(MultiLineMark),
	MultLineComment(MultiLineMark, bool, Option<u8>),
}

/**
 * Filter for removing comments from preprocessed C/C++ code.
 */
pub struct CommentsRemover<R> {
	r: R,
	// Parser state
	state: State,
	// Buffer with size/position
	buffer: Vec<u8>,
	offset: usize,
	limit: usize,
}

impl<R: Read> CommentsRemover<R> {
	pub fn new(r: R) -> CommentsRemover<R> {
		CommentsRemover::new_with_buffer(r, 4096)
	}

	pub fn new_with_buffer(r: R, buffer_size: usize) -> CommentsRemover<R> {
		assert!(buffer_size > 0);
		CommentsRemover {
			r: r,
			state: State::Code(MultiLineMark::None, None),
			buffer: vec! [0; buffer_size],
			offset: 0,
			limit: 0,
		}
	}

	fn preload(&mut self) -> Result<bool, Error> {
		if self.offset == self.limit {
			self.offset = 0;
			self.limit = 0;
		}
		let buffer_size = self.buffer.len();
		if self.limit < buffer_size {
			let size = try! (self.r.read(&mut self.buffer[self.offset..buffer_size]));
			self.limit += size;
		}
		Ok(self.limit > 0)
	}

	fn flush(&mut self, buf: &mut [u8], offset: usize) -> usize {
		match self.state {
			State::Escape(_) | State::SingleLineComment(_) | State::Quote(_, None) | State::Code(_, _) | State::MultLineComment(_, _, None) => offset,
			State::MultLineComment(multiline, end, Some(c)) => {
				self.state = State::MultLineComment(multiline, end, None);
				buf[offset] = c;
			 	offset + 1
			}
			State::Quote(q, Some(c)) => {
				self.state = State::Quote(q, None);
				buf[offset] = c;
			 	offset + 1
			}
		}
	}
}

impl<R: Read> Read for CommentsRemover<R> {
	fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
		if buf.len() == 0 {
			return Ok(0);
		}
		let mut index = 0;
		while index < buf.len() {
			index = self.flush(buf, index);
			if index == buf.len() {
				break;
			}
			assert!(index < buf.len());
			if self.offset >= self.limit {
				if try! (self.preload()) {
					continue;
				}
				match self.state {
					State::Code(_, None) => {
						break;
					}
					State::Code(_, Some(last)) => {
						buf[index] = last;
						index += 1;
						self.state = State::Code(MultiLineMark::None, None);
						break;
					}
					_ => {
						return Err(Error::new(ErrorKind::InvalidInput, "Unexpected end of data"));
					}
				}
			}
			let c = self.buffer[self.offset];
			self.offset += 1;
			self.state = match self.state {
				State::Code(multiline, last) => {
					match c {
						b'"' | b'\'' => {
							match last {
								Some(last) => {
									buf[index] = last;
									index += 1;
								}
								None => {
								}
							}
							State::Quote(c, Some(c))
						}
						b'/' if last == Some(b'/') => State::SingleLineComment(multiline),
						b'*' if last == Some(b'/') => State::MultLineComment(multiline, false, None),
						c => {
							match last {
								Some(last) => {
									buf[index] = last;
									index += 1;
								}
								None => {
								}
							}
							self.state = State::Code(match c {
								b'/' => multiline,
								b'\\' => MultiLineMark::Space,
								b'\t' | b' ' if multiline == MultiLineMark::Space => MultiLineMark::Space,
								b'\t' | b' ' if multiline == MultiLineMark::None => MultiLineMark::None,
								b'\t' | b' '  => MultiLineMark::NewLine,
								b'\n' if multiline == MultiLineMark::Space => MultiLineMark::Lf,
								b'\n' if multiline == MultiLineMark::Cr => MultiLineMark::Cr,
								b'\r' if multiline == MultiLineMark::Space => MultiLineMark::Cr,
								b'\r' if multiline == MultiLineMark::Lf => MultiLineMark::Lf,
								_ => MultiLineMark::None,
							}, Some(c));
							continue;
						}
					}
				}
				State::Escape(q) => {
					State::Quote(q, Some(c))
				}
				State::Quote(q, last) => {
					assert!(last.is_none());
					buf[index] = c;
					index += 1;
					match c {
						b'\\' => State::Escape(q),
						c if c == q => State::Code(MultiLineMark::None, None),
						_ => State::Quote(q, None),
					}
				}
				State::SingleLineComment(multiline) => {
					match c {
						b'\n' | b'\r' => State::Code(multiline, Some(c)),
						_ => State::SingleLineComment(multiline),
					}
				}
				State::MultLineComment(multiline, end, last) => {
					assert!(last.is_none());
					match c {
						b'/' if end => State::Code(multiline, None),
						b'*' => State::MultLineComment(multiline, true, None),
						b'\n' | b'\r' => {
							match multiline {
								MultiLineMark::NewLine => {
									buf[index] = b'\\';
									index += 1;
									State::MultLineComment(match c {
										b'\n' => MultiLineMark::Lf,
										b'\r' => MultiLineMark::Cr,
										_ => MultiLineMark::None,
									}, false, Some(c))
								}
								MultiLineMark::Space => {
									State::MultLineComment(match c {
										b'\n' => MultiLineMark::Lf,
										b'\r' => MultiLineMark::Cr,
										_ => MultiLineMark::None,
									}, false, Some(c))
								}
								MultiLineMark::Lf if c == b'\n' => {
									buf[index] = b'\\';
									index += 1;
									State::MultLineComment(multiline, false, Some(c))
								}
								MultiLineMark::Cr if c == b'\r' => {
									buf[index] = b'\\';
									index += 1;
									State::MultLineComment(multiline, false, Some(c))
								}
							    _ => {
							    	State::MultLineComment(multiline, false, Some(c))
							    }
							}
						}
						_ => State::MultLineComment(multiline, false, None),
					}
				}
			}
		}
		if index < buf.len() {
			index = self.flush(buf, index);
		}
		return Ok(index);
	}
}

#[cfg(test)]
mod test {
	use std::io::{Read, Write, Cursor};
	use super::CommentsRemover;

	fn check_filter_pass(original: &str, expected: &str, block_size: usize) {
		let mut stream: Vec<u8> = Vec::new();
		stream.write(original.as_bytes()).unwrap();

		let mut filter = CommentsRemover::new_with_buffer(Cursor::new(stream), block_size);
		let mut actual = Vec::new();
		let mut buffer = vec![0; block_size];
		loop {
			let size = filter.read(&mut buffer).unwrap();
			if size == 0 {
				break;
			}
			actual.write(&buffer[0..size]).unwrap();
			assert!(actual.len() <= expected.len() * 2);
		}
		assert_eq!(expected, String::from_utf8(actual).unwrap());
	}

	fn check_filter(original: &str, expected: &str) {
		check_filter_pass(original, expected, expected.len());
		check_filter_pass(original, expected, original.len());
		check_filter_pass(original, expected, 1);
		check_filter_pass(original, expected, 3);
		check_filter_pass(original, expected, 10);
		check_filter_pass(original, expected, expected.len() - 1);
		check_filter_pass(original, expected, original.len() - 1);
	}

	#[test]
	fn test_filter_no_comments() {
		let source = r#"#line 1 "sample.cpp"
#line 1 "e:/work/octobuild/test_cl/sample header.h"
# pragma once
void hello();
#line 2 "sample.cpp"

int main(int argc, char **argv) {
	return 0;
}
"#;
		check_filter(source, source);
	}

	#[test]
	fn test_filter_simple_comments() {
		check_filter(
r#"// Some data
#line 1 "e:/work//octobuild/test_cl/sample /* header */.h"
# pragma once /*/ foo */
void hello();
//#line 2 "sample.cpp"

int main(int argc, char **argv /* // Arguments */) {
/*
 * Multiline
 */
	return 0;
}
"#,
r#"
#line 1 "e:/work//octobuild/test_cl/sample /* header */.h"
# pragma once
void hello();

int main(int argc, char **argv ) {

	return 0;
}
"#
		);
	}

	#[test]
	fn test_filter_define_comments() {
		check_filter(
r#"#define A "A1" \
   /* Foo
Bar */ \
          "A2"

#define B "B1" \  /* Buzz */ /*X*//* Foo
Bar */ \
          "B2"

int main() {
	/** Foo
	 */
    return 0;
}
"#,
r#"#define A "A1" \
   \
 \
          "A2"

#define B "B1" \
 \
          "B2"

int main() {

    return 0;
}
"#
		);
	}
}
<|endoftext|>


<commit_before>use once_cell::sync::OnceCell;
use std::collections::HashMap;
use std::rc::Rc;

use crate::bbox::BoundingBox;
use crate::coord_units::CoordUnits;
use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::filter::UserSpaceFilter;
use crate::paint_server::UserSpacePaintSource;
use crate::parsers::CustomIdent;
use crate::properties::ColorInterpolationFilters;
use crate::rect::{IRect, Rect};
use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};
use crate::transform::Transform;

use super::error::FilterError;
use super::Input;

/// A filter primitive output.
#[derive(Debug, Clone)]
pub struct FilterOutput {
    /// The surface after the filter primitive was applied.
    pub surface: SharedImageSurface,

    /// The filter primitive subregion.
    pub bounds: IRect,
}

/// A filter primitive result.
#[derive(Debug, Clone)]
pub struct FilterResult {
    /// The name of this result: the value of the `result` attribute.
    pub name: Option<CustomIdent>,

    /// The output.
    pub output: FilterOutput,
}

/// An input to a filter primitive.
#[derive(Debug, Clone)]
pub enum FilterInput {
    /// One of the standard inputs.
    StandardInput(SharedImageSurface),
    /// Output of another filter primitive.
    PrimitiveOutput(FilterOutput),
}

/// The filter rendering context.
pub struct FilterContext {
    /// Paint source for primitives which have an input value equal to `StrokePaint`.
    stroke_paint: Rc<UserSpacePaintSource>,
    /// Paint source for primitives which have an input value equal to `FillPaint`.
    fill_paint: Rc<UserSpacePaintSource>,

    /// The source graphic surface.
    source_surface: SharedImageSurface,
    /// Output of the last filter primitive.
    last_result: Option<FilterOutput>,
    /// Surfaces of the previous filter primitives by name.
    previous_results: HashMap<CustomIdent, FilterOutput>,

    /// Input surface for primitives that require an input of `BackgroundImage` or `BackgroundAlpha`. Computed lazily.
    background_surface: OnceCell<Result<SharedImageSurface, FilterError>>,

    // Input surface for primitives that require an input of `StrokePaint`, Computed lazily.
    stroke_paint_surface: OnceCell<Result<SharedImageSurface, FilterError>>,

    // Input surface for primitives that require an input of `FillPaint`, Computed lazily.
    fill_paint_surface: OnceCell<Result<SharedImageSurface, FilterError>>,

    /// Primtive units
    primitive_units: CoordUnits,
    /// The filter effects region.
    effects_region: Rect,

    /// The filter element affine matrix.
    ///
    /// If `filterUnits == userSpaceOnUse`, equal to the drawing context matrix, so, for example,
    /// if the target node is in a group with `transform="translate(30, 20)"`, this will be equal
    /// to a matrix that translates to 30, 20 (and does not scale). Note that the target node
    /// bounding box isn't included in the computations in this case.
    ///
    /// If `filterUnits == objectBoundingBox`, equal to the target node bounding box matrix
    /// multiplied by the drawing context matrix, so, for example, if the target node is in a group
    /// with `transform="translate(30, 20)"` and also has `x="1", y="1", width="50", height="50"`,
    /// this will be equal to a matrix that translates to 31, 21 and scales to 50, 50.
    ///
    /// This is to be used in conjunction with setting the viewbox size to account for the scaling.
    /// For `filterUnits == userSpaceOnUse`, the viewbox will have the actual resolution size, and
    /// for `filterUnits == objectBoundingBox`, the viewbox will have the size of 1, 1.
    _affine: Transform,

    /// The filter primitive affine matrix.
    ///
    /// See the comments for `_affine`, they largely apply here.
    paffine: Transform,
}

impl FilterContext {
    /// Creates a new `FilterContext`.
    pub fn new(
        filter: &UserSpaceFilter,
        stroke_paint: Rc<UserSpacePaintSource>,
        fill_paint: Rc<UserSpacePaintSource>,
        source_surface: &SharedImageSurface,
        draw_transform: Transform,
        node_bbox: BoundingBox,
    ) -> Result<Self, FilterError> {
        // The rect can be empty (for example, if the filter is applied to an empty group).
        // However, with userSpaceOnUse it's still possible to create images with a filter.
        let bbox_rect = node_bbox.rect.unwrap_or_default();

        let affine = match filter.filter_units {
            CoordUnits::UserSpaceOnUse => draw_transform,
            CoordUnits::ObjectBoundingBox => Transform::new_unchecked(
                bbox_rect.width(),
                0.0,
                0.0,
                bbox_rect.height(),
                bbox_rect.x0,
                bbox_rect.y0,
            )
            .post_transform(&draw_transform),
        };

        let paffine = match filter.primitive_units {
            CoordUnits::UserSpaceOnUse => draw_transform,
            CoordUnits::ObjectBoundingBox => Transform::new_unchecked(
                bbox_rect.width(),
                0.0,
                0.0,
                bbox_rect.height(),
                bbox_rect.x0,
                bbox_rect.y0,
            )
            .post_transform(&draw_transform),
        };

        if !(affine.is_invertible() && paffine.is_invertible()) {
            return Err(FilterError::InvalidParameter(
                "transform is not invertible".to_string(),
            ));
        }

        let effects_region = {
            let mut bbox = BoundingBox::new();
            let other_bbox = BoundingBox::new()
                .with_transform(affine)
                .with_rect(filter.rect);

            // At this point all of the previous viewbox and matrix business gets converted to pixel
            // coordinates in the final surface, because bbox is created with an identity transform.
            bbox.insert(&other_bbox);

            // Finally, clip to the width and height of our surface.
            let (width, height) = (source_surface.width(), source_surface.height());
            let rect = Rect::from_size(f64::from(width), f64::from(height));
            let other_bbox = BoundingBox::new().with_rect(rect);
            bbox.clip(&other_bbox);

            bbox.rect.unwrap()
        };

        Ok(Self {
            stroke_paint,
            fill_paint,
            source_surface: source_surface.clone(),
            last_result: None,
            previous_results: HashMap::new(),
            background_surface: OnceCell::new(),
            stroke_paint_surface: OnceCell::new(),
            fill_paint_surface: OnceCell::new(),
            primitive_units: filter.primitive_units,
            effects_region,
            _affine: affine,
            paffine,
        })
    }

    /// Returns the surface corresponding to the source graphic.
    #[inline]
    pub fn source_graphic(&self) -> &SharedImageSurface {
        &self.source_surface
    }

    /// Returns the surface corresponding to the background image snapshot.
    fn background_image(&self, draw_ctx: &DrawingCtx) -> Result<SharedImageSurface, FilterError> {
        let res = self.background_surface.get_or_init(|| {
            draw_ctx
                .get_snapshot(self.source_surface.width(), self.source_surface.height())
                .map_err(FilterError::CairoError)
        });

        res.as_ref().map(|s| s.clone()).map_err(|e| e.clone())
    }

    /// Returns a surface filled with the current stroke's paint, for `StrokePaint` inputs in primitives.
    ///
    /// Filter Effects 1: <https://www.w3.org/TR/filter-effects/#attr-valuedef-in-strokepaint>
    fn stroke_paint_image(
        &self,
        acquired_nodes: &mut AcquiredNodes<'_>,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<SharedImageSurface, FilterError> {
        let res = self.stroke_paint_surface.get_or_init(|| {
            Ok(draw_ctx.get_paint_source_surface(
                self.source_surface.width(),
                self.source_surface.height(),
                acquired_nodes,
                &self.stroke_paint,
            )?)
        });

        res.as_ref().map(|s| s.clone()).map_err(|e| e.clone())
    }

    /// Returns a surface filled with the current fill's paint, for `FillPaint` inputs in primitives.
    ///
    /// Filter Effects 1: <https://www.w3.org/TR/filter-effects/#attr-valuedef-in-fillpaint>
    fn fill_paint_image(
        &self,
        acquired_nodes: &mut AcquiredNodes<'_>,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<SharedImageSurface, FilterError> {
        let res = self.fill_paint_surface.get_or_init(|| {
            Ok(draw_ctx.get_paint_source_surface(
                self.source_surface.width(),
                self.source_surface.height(),
                acquired_nodes,
                &self.fill_paint,
            )?)
        });

        res.as_ref().map(|s| s.clone()).map_err(|e| e.clone())
    }

    /// Converts this `FilterContext` into the surface corresponding to the output of the filter
    /// chain.
    ///
    /// The returned surface is in the sRGB color space.
    // TODO: sRGB conversion should probably be done by the caller.
    #[inline]
    pub fn into_output(self) -> Result<SharedImageSurface, cairo::Error> {
        match self.last_result {
            Some(FilterOutput { surface, bounds }) => surface.to_srgb(bounds),
            None => SharedImageSurface::empty(
                self.source_surface.width(),
                self.source_surface.height(),
                SurfaceType::AlphaOnly,
            ),
        }
    }

    /// Stores a filter primitive result into the context.
    #[inline]
    pub fn store_result(&mut self, result: FilterResult) {
        if let Some(name) = result.name {
            self.previous_results.insert(name, result.output.clone());
        }

        self.last_result = Some(result.output);
    }

    /// Returns the paffine matrix.
    #[inline]
    pub fn paffine(&self) -> Transform {
        self.paffine
    }

    /// Returns the primitive units.
    #[inline]
    pub fn primitive_units(&self) -> CoordUnits {
        self.primitive_units
    }

    /// Returns the filter effects region.
    #[inline]
    pub fn effects_region(&self) -> Rect {
        self.effects_region
    }

    /// Get a filter primitive's default input as if its `in=\"...\"` were not specified.
    ///
    /// Per https://drafts.fxtf.org/filter-effects/#element-attrdef-filter-primitive-in,
    /// "References to non-existent results will be treated as if no result was
    /// specified".  That is, fall back to the last result in the filter chain, or if this
    /// is the first in the chain, just use SourceGraphic.
    fn get_unspecified_input(&self) -> FilterInput {
        if let Some(output) = self.last_result.as_ref() {
            FilterInput::PrimitiveOutput(output.clone())
        } else {
            FilterInput::StandardInput(self.source_graphic().clone())
        }
    }

    /// Retrieves the filter input surface according to the SVG rules.
    fn get_input_raw(
        &self,
        acquired_nodes: &mut AcquiredNodes<'_>,
        draw_ctx: &mut DrawingCtx,
        in_: &Input,
    ) -> Result<FilterInput, FilterError> {
        match *in_ {
            Input::Unspecified => Ok(self.get_unspecified_input()),

            Input::SourceGraphic => Ok(FilterInput::StandardInput(self.source_graphic().clone())),

            Input::SourceAlpha => self
                .source_graphic()
                .extract_alpha(self.effects_region().into())
                .map_err(FilterError::CairoError)
                .map(FilterInput::StandardInput),

            Input::BackgroundImage => self
                .background_image(draw_ctx)
                .map(FilterInput::StandardInput),

            Input::BackgroundAlpha => self
                .background_image(draw_ctx)
                .and_then(|surface| {
                    surface
                        .extract_alpha(self.effects_region().into())
                        .map_err(FilterError::CairoError)
                })
                .map(FilterInput::StandardInput),

            Input::FillPaint => self
                .fill_paint_image(acquired_nodes, draw_ctx)
                .map(FilterInput::StandardInput),

            Input::StrokePaint => self
                .stroke_paint_image(acquired_nodes, draw_ctx)
                .map(FilterInput::StandardInput),

            Input::FilterOutput(ref name) => self
                .previous_results
                .get(name)
                .cloned()
                .map(FilterInput::PrimitiveOutput)
                .ok_or(FilterError::InvalidInput),
        }
    }

    /// Retrieves the filter input surface according to the SVG rules.
    ///
    /// The surface will be converted to the color space specified by `color_interpolation_filters`.
    pub fn get_input(
        &self,
        acquired_nodes: &mut AcquiredNodes<'_>,
        draw_ctx: &mut DrawingCtx,
        in_: &Input,
        color_interpolation_filters: ColorInterpolationFilters,
    ) -> Result<FilterInput, FilterError> {
        let raw = self.get_input_raw(acquired_nodes, draw_ctx, in_)?;

        // Convert the input surface to the desired format.
        let (surface, bounds) = match raw {
            FilterInput::StandardInput(ref surface) => (surface, self.effects_region().into()),
            FilterInput::PrimitiveOutput(FilterOutput {
                ref surface,
                ref bounds,
            }) => (surface, *bounds),
        };

        let surface = match color_interpolation_filters {
            ColorInterpolationFilters::Auto => Ok(surface.clone()),
            ColorInterpolationFilters::LinearRgb => surface.to_linear_rgb(bounds),
            ColorInterpolationFilters::Srgb => surface.to_srgb(bounds),
        };

        surface
            .map_err(FilterError::CairoError)
            .map(|surface| match raw {
                FilterInput::StandardInput(_) => FilterInput::StandardInput(surface),
                FilterInput::PrimitiveOutput(ref output) => {
                    FilterInput::PrimitiveOutput(FilterOutput { surface, ..*output })
                }
            })
    }
}

impl FilterInput {
    /// Retrieves the surface from `FilterInput`.
    #[inline]
    pub fn surface(&self) -> &SharedImageSurface {
        match *self {
            FilterInput::StandardInput(ref surface) => surface,
            FilterInput::PrimitiveOutput(FilterOutput { ref surface, .. }) => surface,
        }
    }
}
<commit_msg>FilterContext::get_raw_input() - If a filter input is not found, fall back correctly per the spec<commit_after>use once_cell::sync::OnceCell;
use std::collections::HashMap;
use std::rc::Rc;

use crate::bbox::BoundingBox;
use crate::coord_units::CoordUnits;
use crate::document::AcquiredNodes;
use crate::drawing_ctx::DrawingCtx;
use crate::filter::UserSpaceFilter;
use crate::paint_server::UserSpacePaintSource;
use crate::parsers::CustomIdent;
use crate::properties::ColorInterpolationFilters;
use crate::rect::{IRect, Rect};
use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};
use crate::transform::Transform;

use super::error::FilterError;
use super::Input;

/// A filter primitive output.
#[derive(Debug, Clone)]
pub struct FilterOutput {
    /// The surface after the filter primitive was applied.
    pub surface: SharedImageSurface,

    /// The filter primitive subregion.
    pub bounds: IRect,
}

/// A filter primitive result.
#[derive(Debug, Clone)]
pub struct FilterResult {
    /// The name of this result: the value of the `result` attribute.
    pub name: Option<CustomIdent>,

    /// The output.
    pub output: FilterOutput,
}

/// An input to a filter primitive.
#[derive(Debug, Clone)]
pub enum FilterInput {
    /// One of the standard inputs.
    StandardInput(SharedImageSurface),
    /// Output of another filter primitive.
    PrimitiveOutput(FilterOutput),
}

/// The filter rendering context.
pub struct FilterContext {
    /// Paint source for primitives which have an input value equal to `StrokePaint`.
    stroke_paint: Rc<UserSpacePaintSource>,
    /// Paint source for primitives which have an input value equal to `FillPaint`.
    fill_paint: Rc<UserSpacePaintSource>,

    /// The source graphic surface.
    source_surface: SharedImageSurface,
    /// Output of the last filter primitive.
    last_result: Option<FilterOutput>,
    /// Surfaces of the previous filter primitives by name.
    previous_results: HashMap<CustomIdent, FilterOutput>,

    /// Input surface for primitives that require an input of `BackgroundImage` or `BackgroundAlpha`. Computed lazily.
    background_surface: OnceCell<Result<SharedImageSurface, FilterError>>,

    // Input surface for primitives that require an input of `StrokePaint`, Computed lazily.
    stroke_paint_surface: OnceCell<Result<SharedImageSurface, FilterError>>,

    // Input surface for primitives that require an input of `FillPaint`, Computed lazily.
    fill_paint_surface: OnceCell<Result<SharedImageSurface, FilterError>>,

    /// Primtive units
    primitive_units: CoordUnits,
    /// The filter effects region.
    effects_region: Rect,

    /// The filter element affine matrix.
    ///
    /// If `filterUnits == userSpaceOnUse`, equal to the drawing context matrix, so, for example,
    /// if the target node is in a group with `transform="translate(30, 20)"`, this will be equal
    /// to a matrix that translates to 30, 20 (and does not scale). Note that the target node
    /// bounding box isn't included in the computations in this case.
    ///
    /// If `filterUnits == objectBoundingBox`, equal to the target node bounding box matrix
    /// multiplied by the drawing context matrix, so, for example, if the target node is in a group
    /// with `transform="translate(30, 20)"` and also has `x="1", y="1", width="50", height="50"`,
    /// this will be equal to a matrix that translates to 31, 21 and scales to 50, 50.
    ///
    /// This is to be used in conjunction with setting the viewbox size to account for the scaling.
    /// For `filterUnits == userSpaceOnUse`, the viewbox will have the actual resolution size, and
    /// for `filterUnits == objectBoundingBox`, the viewbox will have the size of 1, 1.
    _affine: Transform,

    /// The filter primitive affine matrix.
    ///
    /// See the comments for `_affine`, they largely apply here.
    paffine: Transform,
}

impl FilterContext {
    /// Creates a new `FilterContext`.
    pub fn new(
        filter: &UserSpaceFilter,
        stroke_paint: Rc<UserSpacePaintSource>,
        fill_paint: Rc<UserSpacePaintSource>,
        source_surface: &SharedImageSurface,
        draw_transform: Transform,
        node_bbox: BoundingBox,
    ) -> Result<Self, FilterError> {
        // The rect can be empty (for example, if the filter is applied to an empty group).
        // However, with userSpaceOnUse it's still possible to create images with a filter.
        let bbox_rect = node_bbox.rect.unwrap_or_default();

        let affine = match filter.filter_units {
            CoordUnits::UserSpaceOnUse => draw_transform,
            CoordUnits::ObjectBoundingBox => Transform::new_unchecked(
                bbox_rect.width(),
                0.0,
                0.0,
                bbox_rect.height(),
                bbox_rect.x0,
                bbox_rect.y0,
            )
            .post_transform(&draw_transform),
        };

        let paffine = match filter.primitive_units {
            CoordUnits::UserSpaceOnUse => draw_transform,
            CoordUnits::ObjectBoundingBox => Transform::new_unchecked(
                bbox_rect.width(),
                0.0,
                0.0,
                bbox_rect.height(),
                bbox_rect.x0,
                bbox_rect.y0,
            )
            .post_transform(&draw_transform),
        };

        if !(affine.is_invertible() && paffine.is_invertible()) {
            return Err(FilterError::InvalidParameter(
                "transform is not invertible".to_string(),
            ));
        }

        let effects_region = {
            let mut bbox = BoundingBox::new();
            let other_bbox = BoundingBox::new()
                .with_transform(affine)
                .with_rect(filter.rect);

            // At this point all of the previous viewbox and matrix business gets converted to pixel
            // coordinates in the final surface, because bbox is created with an identity transform.
            bbox.insert(&other_bbox);

            // Finally, clip to the width and height of our surface.
            let (width, height) = (source_surface.width(), source_surface.height());
            let rect = Rect::from_size(f64::from(width), f64::from(height));
            let other_bbox = BoundingBox::new().with_rect(rect);
            bbox.clip(&other_bbox);

            bbox.rect.unwrap()
        };

        Ok(Self {
            stroke_paint,
            fill_paint,
            source_surface: source_surface.clone(),
            last_result: None,
            previous_results: HashMap::new(),
            background_surface: OnceCell::new(),
            stroke_paint_surface: OnceCell::new(),
            fill_paint_surface: OnceCell::new(),
            primitive_units: filter.primitive_units,
            effects_region,
            _affine: affine,
            paffine,
        })
    }

    /// Returns the surface corresponding to the source graphic.
    #[inline]
    pub fn source_graphic(&self) -> &SharedImageSurface {
        &self.source_surface
    }

    /// Returns the surface corresponding to the background image snapshot.
    fn background_image(&self, draw_ctx: &DrawingCtx) -> Result<SharedImageSurface, FilterError> {
        let res = self.background_surface.get_or_init(|| {
            draw_ctx
                .get_snapshot(self.source_surface.width(), self.source_surface.height())
                .map_err(FilterError::CairoError)
        });

        res.as_ref().map(|s| s.clone()).map_err(|e| e.clone())
    }

    /// Returns a surface filled with the current stroke's paint, for `StrokePaint` inputs in primitives.
    ///
    /// Filter Effects 1: <https://www.w3.org/TR/filter-effects/#attr-valuedef-in-strokepaint>
    fn stroke_paint_image(
        &self,
        acquired_nodes: &mut AcquiredNodes<'_>,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<SharedImageSurface, FilterError> {
        let res = self.stroke_paint_surface.get_or_init(|| {
            Ok(draw_ctx.get_paint_source_surface(
                self.source_surface.width(),
                self.source_surface.height(),
                acquired_nodes,
                &self.stroke_paint,
            )?)
        });

        res.as_ref().map(|s| s.clone()).map_err(|e| e.clone())
    }

    /// Returns a surface filled with the current fill's paint, for `FillPaint` inputs in primitives.
    ///
    /// Filter Effects 1: <https://www.w3.org/TR/filter-effects/#attr-valuedef-in-fillpaint>
    fn fill_paint_image(
        &self,
        acquired_nodes: &mut AcquiredNodes<'_>,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<SharedImageSurface, FilterError> {
        let res = self.fill_paint_surface.get_or_init(|| {
            Ok(draw_ctx.get_paint_source_surface(
                self.source_surface.width(),
                self.source_surface.height(),
                acquired_nodes,
                &self.fill_paint,
            )?)
        });

        res.as_ref().map(|s| s.clone()).map_err(|e| e.clone())
    }

    /// Converts this `FilterContext` into the surface corresponding to the output of the filter
    /// chain.
    ///
    /// The returned surface is in the sRGB color space.
    // TODO: sRGB conversion should probably be done by the caller.
    #[inline]
    pub fn into_output(self) -> Result<SharedImageSurface, cairo::Error> {
        match self.last_result {
            Some(FilterOutput { surface, bounds }) => surface.to_srgb(bounds),
            None => SharedImageSurface::empty(
                self.source_surface.width(),
                self.source_surface.height(),
                SurfaceType::AlphaOnly,
            ),
        }
    }

    /// Stores a filter primitive result into the context.
    #[inline]
    pub fn store_result(&mut self, result: FilterResult) {
        if let Some(name) = result.name {
            self.previous_results.insert(name, result.output.clone());
        }

        self.last_result = Some(result.output);
    }

    /// Returns the paffine matrix.
    #[inline]
    pub fn paffine(&self) -> Transform {
        self.paffine
    }

    /// Returns the primitive units.
    #[inline]
    pub fn primitive_units(&self) -> CoordUnits {
        self.primitive_units
    }

    /// Returns the filter effects region.
    #[inline]
    pub fn effects_region(&self) -> Rect {
        self.effects_region
    }

    /// Get a filter primitive's default input as if its `in=\"...\"` were not specified.
    ///
    /// Per https://drafts.fxtf.org/filter-effects/#element-attrdef-filter-primitive-in,
    /// "References to non-existent results will be treated as if no result was
    /// specified".  That is, fall back to the last result in the filter chain, or if this
    /// is the first in the chain, just use SourceGraphic.
    fn get_unspecified_input(&self) -> FilterInput {
        if let Some(output) = self.last_result.as_ref() {
            FilterInput::PrimitiveOutput(output.clone())
        } else {
            FilterInput::StandardInput(self.source_graphic().clone())
        }
    }

    /// Retrieves the filter input surface according to the SVG rules.
    fn get_input_raw(
        &self,
        acquired_nodes: &mut AcquiredNodes<'_>,
        draw_ctx: &mut DrawingCtx,
        in_: &Input,
    ) -> Result<FilterInput, FilterError> {
        match *in_ {
            Input::Unspecified => Ok(self.get_unspecified_input()),

            Input::SourceGraphic => Ok(FilterInput::StandardInput(self.source_graphic().clone())),

            Input::SourceAlpha => self
                .source_graphic()
                .extract_alpha(self.effects_region().into())
                .map_err(FilterError::CairoError)
                .map(FilterInput::StandardInput),

            Input::BackgroundImage => self
                .background_image(draw_ctx)
                .map(FilterInput::StandardInput),

            Input::BackgroundAlpha => self
                .background_image(draw_ctx)
                .and_then(|surface| {
                    surface
                        .extract_alpha(self.effects_region().into())
                        .map_err(FilterError::CairoError)
                })
                .map(FilterInput::StandardInput),

            Input::FillPaint => self
                .fill_paint_image(acquired_nodes, draw_ctx)
                .map(FilterInput::StandardInput),

            Input::StrokePaint => self
                .stroke_paint_image(acquired_nodes, draw_ctx)
                .map(FilterInput::StandardInput),

            Input::FilterOutput(ref name) => {
                let input = match self.previous_results.get(name).cloned() {
                    Some(filter_output) => {
                        // Happy path: we found a previous primitive's named output, so pass it on.
                        FilterInput::PrimitiveOutput(filter_output)
                    }

                    None => {
                        // Fallback path: we didn't find a primitive's output by the
                        // specified name, so fall back to using an unspecified output.
                        // Per the spec, "References to non-existent results will be
                        // treated as if no result was specified." -
                        // https://drafts.fxtf.org/filter-effects/#element-attrdef-filter-primitive-in
                        self.get_unspecified_input()
                    }
                };

                Ok(input)
            }
        }
    }

    /// Retrieves the filter input surface according to the SVG rules.
    ///
    /// The surface will be converted to the color space specified by `color_interpolation_filters`.
    pub fn get_input(
        &self,
        acquired_nodes: &mut AcquiredNodes<'_>,
        draw_ctx: &mut DrawingCtx,
        in_: &Input,
        color_interpolation_filters: ColorInterpolationFilters,
    ) -> Result<FilterInput, FilterError> {
        let raw = self.get_input_raw(acquired_nodes, draw_ctx, in_)?;

        // Convert the input surface to the desired format.
        let (surface, bounds) = match raw {
            FilterInput::StandardInput(ref surface) => (surface, self.effects_region().into()),
            FilterInput::PrimitiveOutput(FilterOutput {
                ref surface,
                ref bounds,
            }) => (surface, *bounds),
        };

        let surface = match color_interpolation_filters {
            ColorInterpolationFilters::Auto => Ok(surface.clone()),
            ColorInterpolationFilters::LinearRgb => surface.to_linear_rgb(bounds),
            ColorInterpolationFilters::Srgb => surface.to_srgb(bounds),
        };

        surface
            .map_err(FilterError::CairoError)
            .map(|surface| match raw {
                FilterInput::StandardInput(_) => FilterInput::StandardInput(surface),
                FilterInput::PrimitiveOutput(ref output) => {
                    FilterInput::PrimitiveOutput(FilterOutput { surface, ..*output })
                }
            })
    }
}

impl FilterInput {
    /// Retrieves the surface from `FilterInput`.
    #[inline]
    pub fn surface(&self) -> &SharedImageSurface {
        match *self {
            FilterInput::StandardInput(ref surface) => surface,
            FilterInput::PrimitiveOutput(FilterOutput { ref surface, .. }) => surface,
        }
    }
}
<|endoftext|>


<commit_before>#[test]
fn it_works() {
}

mod SVM {
    enum Exp {
        Number(int)
    }

    struct Engine {
        stack: Vec<Exp>
    }
}
<commit_msg>Add list stuff<commit_after>#[test]
fn it_works() {
}

mod SVM {
    /// Singly-linked cons list.
    ///
    /// This is used internally to represent list primitives in the machine.
    pub enum List<T> {
        Cons(T ~List<T>),
        Nil
    }

    /// Public implementation for List.
    pub impl List<T> {

        pub fn new() -> List<T> {
            Nil
        }

    }

    enum Exp {
        Number(int)
    }

    struct Engine {
        stack: Vec<Exp>
    }
}
<|endoftext|>


<commit_before>extern crate graphics_impl;

use std::num::Bounded;

static MAX_HORIZONTAL_PIXELS : uint = 128;
static MAX_VERTICAL_PIXELS : uint = 64;

struct Dimensions {
    width:  uint,
    height: uint
}

static SCHIP_dimensions :Dimensions = Dimensions{ width: 128, height: 64};
static  CHIP_dimensions :Dimensions = Dimensions{ width: 64,  height: 32};

/* Either CHIP or Super CHIP mode */
pub enum Mode { CHIP,
                SCHIP}

impl Mode {

    fn get_width(&self) -> uint {
        match *self {
            CHIP => CHIP_dimensions.width,
            SCHIP => SCHIP_dimensions.width
        }
    }

    fn get_height(&self) -> uint {
        match *self {
            CHIP  =>  CHIP_dimensions.width,
            SCHIP => SCHIP_dimensions.height
        }
    }
}

pub struct Graphics {
    mode :Mode,
    screen : [[bool, ..MAX_HORIZONTAL_PIXELS], ..MAX_VERTICAL_PIXELS],
    out : graphics_impl::Screen
}

impl Graphics {

    pub fn new() -> Graphics {
        Graphics { mode: CHIP,
                   /* Initialize all pixels to blank */
                   screen: [[false, ..MAX_HORIZONTAL_PIXELS], ..MAX_VERTICAL_PIXELS],
                   out: graphics_impl::Screen::new(256, 128,
                    CHIP_dimensions.width, CHIP_dimensions.height)
         }
    }

    pub fn set_mode(&mut self, mode:Mode) {
        self.mode = mode;
        self.out.set_x_max(mode.get_width());
        self.out.set_y_max(mode.get_height());
    }

    pub fn draw_pix(&mut self, x:uint, y:uint, state:bool) {
        self.screen[y][x] = state;
    }

    fn to_bit_vec<N: Unsigned + Int>(num :N) -> Vec<u8> {
        let max : N = Bounded::max_value();

        let max_u = match max.to_uint() {
            Some(val) => val,
            None => fail!("cannot convert num to uint")
        };

        let bit_count = max_u.count_ones();
        let largest_bit = max_u - (max_u >> 1);

        let num_u = match num.to_uint() {
            Some(val) => val,
            None => fail!("cannot convert num to uint")
        };
        Vec::from_fn(bit_count,
            |idx| if num_u & (largest_bit >> idx ) != 0 {1u8} else {0u8} )
    }

    pub fn draw_line<N: Unsigned + Int>(&mut self, startx:u8, starty:u8, line:N) -> bool {

       let  pixel_states :Vec<bool>  = Graphics::to_bit_vec(line)
                       .iter()
                       .map(|&x| if x == 0 {false} else {true})
                       .collect();

        let current_states = self.screen[starty as uint]
                        .mut_slice_from(startx as uint);

        let mut zipped_states = current_states.mut_iter().zip(pixel_states.iter());
        /* Set pixel to old pixel xor new pixel */
        for (old, new) in zipped_states { *old = *old ^ *new;}

        /* Rust's type inference sucks at the moment :/ */
        let and_true_states: Vec<(&mut bool, &bool)> =
            zipped_states.filter(|&(&old, &new)| old && new == true).collect();
        /* If any of old and new pixels were both true then a pixel was unset */
        return and_true_states.len() > 0
   }

    pub fn scroll_right(&mut self, n:u8) {
        for y in range(0, self.mode.get_height()) {
            /*TODO research how to properly use a decreasing
             * iterator in a range instead of this solution */
            for x1 in range(-((self.mode.get_width() - 1) as int), - (n as int - 1)) {
                let x = -x1;
                let set = self.screen[y][x as uint - n as uint];
                self.draw_pix(x as uint, y, set);
            }
            for x  in range(0, n as uint) {
                self.draw_pix(x as uint, y, false);
            }
        }
    }

    pub fn scroll_left(&mut self, n:u8) {
        let x_max = self.mode.get_width();

        for y in range(0 , self.mode.get_height()) {
            for x in range(0, x_max - n as uint) {
                let set = self.screen[y][x + n as uint];
                self.draw_pix(x, y, set);
            }
            for x in range(x_max - n as uint, x_max) {
                self.draw_pix(x, y, false);
            }
        }
    }

    pub fn scroll_down(&mut self, n:u8) {
        let y_max = self.mode.get_height();
        for x in range(0, self.mode.get_width()) {
            for y1 in range(-((y_max - 1) as int), - (n as int - 1)) {
                let y = -y1;
                let set = self.screen[y as uint - n as uint][x];
                self.draw_pix(x, y as uint, set);
            }
            for y  in range(0, n as uint) {
                self.draw_pix(x, y as uint, false);
            }
        }
    }

    pub fn clear_screen(&mut self) {
        for y in range(0, MAX_VERTICAL_PIXELS) {
            for x in range(0, MAX_HORIZONTAL_PIXELS) {
                self.screen[y][x] = false;
            }
        }
        self.out.clear_screen();
    }

    pub fn show(&mut self) {
        for y in range(0, MAX_VERTICAL_PIXELS) {
            for x in range(0, MAX_HORIZONTAL_PIXELS) {
                self.out.draw_pix(x as int, y as int, self.screen[y][x]);
            }
        }
        self.out.show();
    }
}
<commit_msg>Replaced hacky negative range in graphics with range_step<commit_after>extern crate graphics_impl;

use std::num::Bounded;
use std::iter::range_step;

static MAX_HORIZONTAL_PIXELS : uint = 128;
static MAX_VERTICAL_PIXELS : uint = 64;

struct Dimensions {
    width:  uint,
    height: uint
}

static SCHIP_dimensions :Dimensions = Dimensions{ width: 128, height: 64};
static  CHIP_dimensions :Dimensions = Dimensions{ width: 64,  height: 32};

/* Either CHIP or Super CHIP mode */
pub enum Mode { CHIP,
                SCHIP}

impl Mode {

    fn get_width(&self) -> uint {
        match *self {
            CHIP => CHIP_dimensions.width,
            SCHIP => SCHIP_dimensions.width
        }
    }

    fn get_height(&self) -> uint {
        match *self {
            CHIP  =>  CHIP_dimensions.width,
            SCHIP => SCHIP_dimensions.height
        }
    }
}

pub struct Graphics {
    mode :Mode,
    screen : [[bool, ..MAX_HORIZONTAL_PIXELS], ..MAX_VERTICAL_PIXELS],
    out : graphics_impl::Screen
}

impl Graphics {

    pub fn new() -> Graphics {
        Graphics { mode: CHIP,
                   /* Initialize all pixels to blank */
                   screen: [[false, ..MAX_HORIZONTAL_PIXELS], ..MAX_VERTICAL_PIXELS],
                   out: graphics_impl::Screen::new(256, 128,
                    CHIP_dimensions.width, CHIP_dimensions.height)
         }
    }

    pub fn set_mode(&mut self, mode:Mode) {
        self.mode = mode;
        self.out.set_x_max(mode.get_width());
        self.out.set_y_max(mode.get_height());
    }

    pub fn draw_pix(&mut self, x:uint, y:uint, state:bool) {
        self.screen[y][x] = state;
    }

    /* Create a bit vector for the supplied number from
     * MS bit to LS bit */
    fn to_bit_vec<N: Unsigned + Int>(num :N) -> Vec<u8> {
        let max : N = Bounded::max_value();

        let max_u = match max.to_uint() {
            Some(val) => val,
            None => fail!("cannot convert num to uint")
        };

        let bit_count = max_u.count_ones();
        let largest_bit = max_u - (max_u >> 1);

        let num_u = match num.to_uint() {
            Some(val) => val,
            None => fail!("cannot convert num to uint")
        };
        Vec::from_fn(bit_count,
            |idx| if num_u & (largest_bit >> idx ) != 0 {1u8} else {0u8} )
    }

    pub fn draw_line<N: Unsigned + Int>(&mut self, startx:u8, starty:u8, line:N) -> bool {

       let  pixel_states :Vec<bool>  = Graphics::to_bit_vec(line)
                       .iter()
                       .map(|&x| if x == 0 {false} else {true})
                       .collect();

        let current_states = self.screen[starty as uint]
                        .mut_slice_from(startx as uint);

        let mut zipped_states = current_states.mut_iter().zip(pixel_states.iter());
        /* Set pixel to old pixel xor new pixel */
        for (old, new) in zipped_states { *old = *old ^ *new;}

        /* Rust's type inference sucks at the moment :/ */
        let and_true_states: Vec<(&mut bool, &bool)> =
            zipped_states.filter(|&(&old, &new)| old && new == true).collect();
        /* If any of old and new pixels were both true then a pixel was unset */
        return and_true_states.len() > 0
   }

    pub fn scroll_right(&mut self, n:u8) {
        let n = n as uint;
        for y in range(0, self.mode.get_height()) {
            for x in range_step(self.mode.get_width() - 1, n  - 1, -1) {
                let set = self.screen[y][x - n];
                self.draw_pix(x, y, set);
            }
            for x  in range(0, n) {
                self.draw_pix(x, y, false);
            }
        }
    }

    pub fn scroll_left(&mut self, n:u8) {
        let x_max = self.mode.get_width();
        let n = n as uint;
        for y in range(0 , self.mode.get_height()) {
            for x in range(0, x_max - n) {
                let set = self.screen[y][x + n];
                self.draw_pix(x, y, set);
            }
            for x in range(x_max - n, x_max) {
                self.draw_pix(x, y, false);
            }
        }
    }

    pub fn scroll_down(&mut self, n:u8) {
        let y_max = self.mode.get_height();
        let n = n as uint;
        for x in range(0, self.mode.get_width()) {
            for y in range_step(y_max - 1, n - 1, -1) {
                let set = self.screen[y - n][x];
                self.draw_pix(x, y, set);
            }
            for y  in range(0, n) {
                self.draw_pix(x, y, false);
            }
        }
    }

    pub fn clear_screen(&mut self) {
        for y in range(0, MAX_VERTICAL_PIXELS) {
            for x in range(0, MAX_HORIZONTAL_PIXELS) {
                self.screen[y][x] = false;
            }
        }
        self.out.clear_screen();
    }

    //TODO improve as iinefficient (causes redrawing of entire screen)
    pub fn show(&mut self) {
        for y in range(0, MAX_VERTICAL_PIXELS) {
            for x in range(0, MAX_HORIZONTAL_PIXELS) {
                self.out.draw_pix(x as int, y as int, self.screen[y][x]);
            }
        }
        self.out.show();
    }
}
<|endoftext|>