Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Remove the servo-gfx submodule

  • Loading branch information...
commit 463b80090ce20a8c3cb79e43281c8925c2e05f39 1 parent 5e70e3b
@pcwalton pcwalton authored
Showing with 5,535 additions and 25 deletions.
  1. +0 −3  .gitmodules
  2. +16 −3 Makefile.in
  3. +2 −1  configure
  4. +1 −17 mk/sub.mk
  5. +0 −1  src/servo-gfx
  6. BIN  src/servo-gfx-2/JosefinSans-SemiBold.ttf
  7. +93 −0 src/servo-gfx-2/OFL.txt
  8. +18 −0 src/servo-gfx-2/color.rs
  9. +28 −0 src/servo-gfx-2/compositor.rs
  10. +117 −0 src/servo-gfx-2/display_list.rs
  11. +499 −0 src/servo-gfx-2/font.rs
  12. +124 −0 src/servo-gfx-2/font_context.rs
  13. +137 −0 src/servo-gfx-2/font_list.rs
  14. +12 −0 src/servo-gfx-2/fontconfig/font_list.rs
  15. +151 −0 src/servo-gfx-2/freetype/font.rs
  16. +33 −0 src/servo-gfx-2/freetype/font_context.rs
  17. +109 −0 src/servo-gfx-2/geometry.rs
  18. +43 −0 src/servo-gfx-2/image/base.rs
  19. +23 −0 src/servo-gfx-2/image/encode/tga.rs
  20. +102 −0 src/servo-gfx-2/image/holder.rs
  21. BIN  src/servo-gfx-2/image/test.jpeg
  22. +9 −0 src/servo-gfx-2/native.rs
  23. +77 −0 src/servo-gfx-2/opts.rs
  24. +161 −0 src/servo-gfx-2/quartz/font.rs
  25. +12 −0 src/servo-gfx-2/quartz/font_context.rs
  26. +56 −0 src/servo-gfx-2/quartz/font_list.rs
  27. +105 −0 src/servo-gfx-2/render_context.rs
  28. +137 −0 src/servo-gfx-2/render_layers.rs
  29. +157 −0 src/servo-gfx-2/render_task.rs
  30. +29 −0 src/servo-gfx-2/resource/file_loader.rs
  31. +38 −0 src/servo-gfx-2/resource/http_loader.rs
  32. +1,101 −0 src/servo-gfx-2/resource/image_cache_task.rs
  33. +150 −0 src/servo-gfx-2/resource/local_image_cache.rs
  34. +157 −0 src/servo-gfx-2/resource/resource_task.rs
  35. +93 −0 src/servo-gfx-2/servo_gfx.rc
  36. +5 −0 src/servo-gfx-2/servo_gfx.rs
  37. +40 −0 src/servo-gfx-2/surface.rs
  38. +9 −0 src/servo-gfx-2/text.rs
  39. +624 −0 src/servo-gfx-2/text/glyph.rs
  40. +177 −0 src/servo-gfx-2/text/harfbuzz/shaper.rs
  41. +17 −0 src/servo-gfx-2/text/shaper.rs
  42. +203 −0 src/servo-gfx-2/text/text_run.rs
  43. +223 −0 src/servo-gfx-2/text/util.rs
  44. +59 −0 src/servo-gfx-2/util/cache.rs
  45. +164 −0 src/servo-gfx-2/util/range.rs
  46. +15 −0 src/servo-gfx-2/util/time.rs
  47. +107 −0 src/servo-gfx-2/util/url.rs
  48. +102 −0 src/servo-gfx-2/util/vec.rs
View
3  .gitmodules
@@ -85,6 +85,3 @@
[submodule "src/skia"]
path = src/skia
url = git://github.com/mozilla-servo/skia.git
-[submodule "src/servo-gfx"]
- path = src/servo-gfx
- url = git://github.com/mozilla-servo/servo-gfx.git
View
19 Makefile.in
@@ -130,12 +130,20 @@ endef
$(foreach submodule,$(CFG_SUBMODULES),\
$(eval $(call DEF_SUBMODULE_RULES,$(submodule))))
-RFLAGS_servo = $(strip $(CFG_RUSTC_FLAGS)) $(addprefix -L $(B)src/,$(DEPS_SUBMODULES))
+DONE_SUBMODULES = $(foreach dep,$(DEPS_SUBMODULES),$(DONE_$(dep)))
+
+RFLAGS_servo_gfx = $(strip $(CFG_RUSTC_FLAGS)) $(addprefix -L $(B)src/,$(DEPS_SUBMODULES))
+SRC_servo_gfx = $(call rwildcard,$(S)src/servo-gfx-2/,*.rs)
+CRATE_servo_gfx = $(S)src/servo-gfx-2/servo_gfx.rc
+DONE_servo_gfx = $(B)src/servo-gfx-2/libservogfx.dummy
+
+DEPS_servo_gfx = $(CRATE_servo_gfx) $(SRC_servo_gfx) $(DONE_SUBMODULES)
+
+RFLAGS_servo = $(strip $(CFG_RUSTC_FLAGS)) $(addprefix -L $(B)src/,$(DEPS_SUBMODULES)) -L $(B)src/servo-gfx-2
SRC_servo = $(call rwildcard,$(S)src/servo/,*.rs)
CRATE_servo = $(S)src/servo/servo.rc
-DONE_SUBMODULES = $(foreach dep,$(DEPS_SUBMODULES),$(DONE_$(dep)))
-DEPS_servo = $(CRATE_servo) $(SRC_servo) $(DONE_SUBMODULES)
+DEPS_servo = $(CRATE_servo) $(SRC_servo) $(DONE_SUBMODULES) $(DONE_servo_gfx)
# rules that depend on having correct meta-target vars (DEPS_CLEAN, DEPS_servo, etc)
include $(S)mk/check.mk
@@ -145,6 +153,11 @@ include $(S)mk/clean.mk
.PHONY: all
all: servo package
+# Servo helper libraries
+
+$(DONE_servo_gfx): $(DEPS_servo_gfx)
+ $(RUSTC) $(RFLAGS_servo_gfx) -o $@ $< && touch $@
+
# Servo binaries
servo: $(DEPS_servo)
View
3  configure
@@ -344,7 +344,7 @@ step_msg "running submodule autoconf scripts"
(cd ${CFG_SRC_DIR}src/mozjs/js/src && "${CFG_AUTOCONF213}") || exit $?
-CFG_SUBMODULES="libwapcaplet rust-wapcaplet rust-harfbuzz rust-opengles skia rust-azure rust-cairo rust-stb-image rust-geom rust-glut rust-layers rust-http-client libparserutils libhubbub libcss rust-netsurfcss rust-css rust-hubbub sharegl rust-mozjs mozjs servo-gfx"
+CFG_SUBMODULES="libwapcaplet rust-wapcaplet rust-harfbuzz rust-opengles skia rust-azure rust-cairo rust-stb-image rust-geom rust-glut rust-layers rust-http-client libparserutils libhubbub libcss rust-netsurfcss rust-css rust-hubbub sharegl rust-mozjs mozjs"
if [ $CFG_OSTYPE = "darwin" ]
then
@@ -365,6 +365,7 @@ do
make_dir ${CFG_BUILD_DIR}src/${i}
done
+make_dir ${CFG_BUILD_DIR}src/servo-gfx-2
make_dir src/test/ref
# TODO: don't run configure on submodules unless necessary. For an example,
View
18 mk/sub.mk
@@ -81,16 +81,6 @@ DEPS_libcss += \
libparserutils \
$(NULL)
-DEPS_servo-gfx += \
- rust-azure \
- rust-cairo \
- rust-freetype \
- rust-geom \
- rust-harfbuzz \
- rust-http-client \
- rust-stb-image \
- $(NULL)
-
# Platform-specific dependencies
ifeq ($(CFG_OSTYPE),darwin)
DEPS_rust-azure += \
@@ -105,7 +95,7 @@ DEPS_rust-cairo += \
rust-core-graphics \
$(NULL)
-DEPS_rust-io-surface += \
+dEPS_rust-io-surface += \
rust-core-foundation \
$(NULL)
@@ -129,12 +119,6 @@ DEPS_rust-layers += \
rust-core-text \
$(NULL)
-DEPS_servo-gfx += \
- rust-core-foundation \
- rust-core-graphics \
- rust-core-text \
- $(NULL)
-
endif
ifeq ($(CFG_OSTYPE),linux)
1  src/servo-gfx
@@ -1 +0,0 @@
-Subproject commit 08d11a2db06e733ce9c84524819d37486a26c160
View
BIN  src/servo-gfx-2/JosefinSans-SemiBold.ttf
Binary file not shown
View
93 src/servo-gfx-2/OFL.txt
@@ -0,0 +1,93 @@
+Copyright (c) 2009, 2010, 2011 Daniel Johnson (<il.basso.buffo@gmail.com>).
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
View
18 src/servo-gfx-2/color.rs
@@ -0,0 +1,18 @@
+use azure::AzFloat;
+use AzColor = azure::azure_hl::Color;
+
+pub type Color = AzColor;
+
+pub fn rgb(r: u8, g: u8, b: u8) -> AzColor {
+ rgba(r, g, b, 1.0)
+}
+
+pub fn rgba(r: u8, g: u8, b: u8, a: float) -> AzColor {
+ AzColor {
+ r: (r as AzFloat) / (255.0 as AzFloat),
+ g: (g as AzFloat) / (255.0 as AzFloat),
+ b: (b as AzFloat) / (255.0 as AzFloat),
+ a: a as AzFloat
+ }
+}
+
View
28 src/servo-gfx-2/compositor.rs
@@ -0,0 +1,28 @@
+use azure::azure_hl::{DrawTarget};
+use geom::rect::Rect;
+
+struct LayerBuffer {
+ draw_target: DrawTarget,
+
+ // The rect in the containing RenderLayer that this represents.
+ rect: Rect<uint>,
+
+ // NB: stride is in pixels, like OpenGL GL_UNPACK_ROW_LENGTH.
+ stride: uint
+}
+
+/// A set of layer buffers. This is an atomic unit used to switch between the front and back
+/// buffers.
+struct LayerBufferSet {
+ buffers: ~[LayerBuffer]
+}
+
+/**
+The interface used to by the renderer to aquire draw targets for
+each rendered frame and submit them to be drawn to the display
+*/
+trait Compositor {
+ fn begin_drawing(next_dt: pipes::Chan<LayerBufferSet>);
+ fn draw(next_dt: pipes::Chan<LayerBufferSet>, +draw_me: LayerBufferSet);
+}
+
View
117 src/servo-gfx-2/display_list.rs
@@ -0,0 +1,117 @@
+use color::{Color, rgb};
+use geometry::Au;
+use image::base::Image;
+use render_context::RenderContext;
+use text::SendableTextRun;
+use util::range::Range;
+
+use azure::azure_hl::DrawTarget;
+use core::dvec::DVec;
+use clone_arc = std::arc::clone;
+use geom::Rect;
+use geom::Point2D;
+use std::arc::ARC;
+
+struct DisplayItemData {
+ bounds : Rect<Au>, // TODO: whose coordinate system should this use?
+}
+
+impl DisplayItemData {
+ static pure fn new(bounds: &Rect<Au>) -> DisplayItemData {
+ DisplayItemData { bounds: copy *bounds }
+ }
+}
+
+pub enum DisplayItem {
+ SolidColor(DisplayItemData, Color),
+ // TODO: need to provide spacing data for text run.
+ // (i.e, to support rendering of CSS 'word-spacing' and 'letter-spacing')
+ // TODO: don't copy text runs, ever.
+ Text(DisplayItemData, ~SendableTextRun, Range, Color),
+ Image(DisplayItemData, ARC<~image::base::Image>),
+ Border(DisplayItemData, Au, Color)
+}
+
+impl DisplayItem {
+ pure fn d(&self) -> &self/DisplayItemData {
+ match *self {
+ SolidColor(ref d, _) => d,
+ Text(ref d, _, _, _) => d,
+ Image(ref d, _) => d,
+ Border(ref d, _, _) => d
+ }
+ }
+
+ fn draw_into_context(&self, ctx: &RenderContext) {
+ match *self {
+ SolidColor(_, color) => ctx.draw_solid_color(&self.d().bounds, color),
+ Text(_, run, range, color) => {
+ let new_run = @run.deserialize(ctx.font_ctx);
+ let font = new_run.font;
+ let origin = self.d().bounds.origin;
+ let baseline_origin = Point2D(origin.x, origin.y + font.metrics.ascent);
+ font.draw_text_into_context(ctx, new_run, range, baseline_origin, color);
+ },
+ Image(_, ref img) => ctx.draw_image(self.d().bounds, clone_arc(img)),
+ Border(_, width, color) => ctx.draw_border(&self.d().bounds, width, color),
+ }
+
+ debug!("%?", {
+ ctx.draw_border(&self.d().bounds, Au::from_px(1), rgb(150, 150, 150));
+ () });
+ }
+
+ static pure fn new_SolidColor(bounds: &Rect<Au>, color: Color) -> DisplayItem {
+ SolidColor(DisplayItemData::new(bounds), color)
+ }
+
+ static pure fn new_Border(bounds: &Rect<Au>, width: Au, color: Color) -> DisplayItem {
+ Border(DisplayItemData::new(bounds), width, color)
+ }
+
+ static pure fn new_Text(bounds: &Rect<Au>,
+ run: ~SendableTextRun,
+ range: Range,
+ color: Color) -> DisplayItem {
+ Text(DisplayItemData::new(bounds), move run, range, color)
+ }
+
+ // ARC should be cloned into ImageData, but Images are not sendable
+ static pure fn new_Image(bounds: &Rect<Au>, image: ARC<~image::base::Image>) -> DisplayItem {
+ Image(DisplayItemData::new(bounds), move image)
+ }
+}
+
+// Dual-mode/freezable.
+pub struct DisplayList {
+ list: ~[~DisplayItem]
+}
+
+trait DisplayListMethods {
+ fn append_item(&mut self, item: ~DisplayItem);
+ fn draw_into_context(ctx: &RenderContext);
+}
+
+impl DisplayList {
+ static fn new() -> DisplayList {
+ DisplayList { list: ~[] }
+ }
+}
+
+impl DisplayList : DisplayListMethods {
+ fn append_item(&mut self, item: ~DisplayItem) {
+ // FIXME(Issue #150): crashes
+ //debug!("Adding display item %u: %?", self.len(), item);
+ self.list.push(move item);
+ }
+
+ fn draw_into_context(ctx: &RenderContext) {
+ debug!("beginning display list");
+ for self.list.each |item| {
+ // FIXME(Issue #150): crashes
+ //debug!("drawing %?", *item);
+ item.draw_into_context(ctx);
+ }
+ debug!("ending display list");
+ }
+}
View
499 src/servo-gfx-2/font.rs
@@ -0,0 +1,499 @@
+use color::Color;
+use geometry::Au;
+use render_context::RenderContext;
+use util::range::Range;
+use text::glyph::{GlyphStore, GlyphIndex};
+use text::{Shaper, TextRun};
+
+use azure::{AzFloat, AzScaledFontRef};
+use azure::azure_hl::{BackendType, ColorPattern};
+use core::dvec::DVec;
+use geom::{Point2D, Rect, Size2D};
+
+// FontHandle encapsulates access to the platform's font API,
+// e.g. quartz, FreeType. It provides access to metrics and tables
+// needed by the text shaper as well as access to the underlying font
+// resources needed by the graphics layer to draw glyphs.
+
+#[cfg(target_os = "macos")]
+pub type FontHandle/& = quartz::font::QuartzFontHandle;
+
+#[cfg(target_os = "linux")]
+pub type FontHandle/& = freetype::font::FreeTypeFontHandle;
+
+pub trait FontHandleMethods {
+ pure fn face_name() -> ~str;
+ pure fn is_italic() -> bool;
+ pure fn boldness() -> CSSFontWeight;
+
+ fn glyph_index(codepoint: char) -> Option<GlyphIndex>;
+ fn glyph_h_advance(GlyphIndex) -> Option<FractionalPixel>;
+ fn get_metrics() -> FontMetrics;
+}
+
+// TODO: `new` should be part of trait FontHandleMethods
+
+// TODO(Issue #163): this is a workaround for static methods and
+// typedefs not working well together. It should be removed.
+
+impl FontHandle {
+ #[cfg(target_os = "macos")]
+ static pub fn new(fctx: &native::FontContextHandle, buf: @~[u8], pt_size: float) -> Result<FontHandle, ()> {
+ quartz::font::QuartzFontHandle::new_from_buffer(fctx, buf, pt_size)
+ }
+
+ #[cfg(target_os = "linux")]
+ static pub fn new(fctx: &native::FontContextHandle, buf: @~[u8], pt_size: float) -> Result<FontHandle, ()> {
+ freetype::font::FreeTypeFontHandle::new(fctx, buf, pt_size)
+ }
+}
+
+// Used to abstract over the shaper's choice of fixed int representation.
+type FractionalPixel = float;
+
+struct FontMetrics {
+ underline_size: Au,
+ underline_offset: Au,
+ leading: Au,
+ x_height: Au,
+ em_size: Au,
+ ascent: Au,
+ descent: Au,
+ max_advance: Au
+}
+
+// TODO: use enum from CSS bindings
+enum CSSFontWeight {
+ FontWeight100,
+ FontWeight200,
+ FontWeight300,
+ FontWeight400,
+ FontWeight500,
+ FontWeight600,
+ FontWeight700,
+ FontWeight800,
+ FontWeight900,
+}
+pub impl CSSFontWeight : cmp::Eq;
+
+pub impl CSSFontWeight {
+ pub pure fn is_bold() -> bool {
+ match self {
+ FontWeight900 | FontWeight800 | FontWeight700 | FontWeight600 => true,
+ _ => false
+ }
+ }
+}
+
+// TODO: eventually this will be split into the specified and used
+// font styles. specified contains uninterpreted CSS font property
+// values, while 'used' is attached to gfx::Font to descript the
+// instance's properties.
+//
+// For now, the cases are differentiated with a typedef
+pub struct FontStyle {
+ pt_size: float,
+ weight: CSSFontWeight,
+ italic: bool,
+ oblique: bool,
+ families: ~str,
+ // TODO: font-stretch, text-decoration, font-variant, size-adjust
+}
+
+// TODO(Issue #181): use deriving for trivial cmp::Eq implementations
+pub impl FontStyle : cmp::Eq {
+ pure fn eq(other: &FontStyle) -> bool {
+ use std::cmp::FuzzyEq;
+
+ self.pt_size.fuzzy_eq(&other.pt_size) &&
+ self.weight == other.weight &&
+ self.italic == other.italic &&
+ self.oblique == other.oblique &&
+ self.families == other.families
+ }
+ pure fn ne(other: &FontStyle) -> bool { !self.eq(other) }
+}
+
+pub type SpecifiedFontStyle = FontStyle;
+pub type UsedFontStyle = FontStyle;
+
+// TODO: move me to layout
+struct ResolvedFont {
+ group: @FontGroup,
+ style: SpecifiedFontStyle,
+}
+
+// FontDescriptor serializes a specific font and used font style
+// options, such as point size.
+
+// It's used to swizzle/unswizzle gfx::Font instances when
+// communicating across tasks, such as the display list between layout
+// and render tasks.
+pub struct FontDescriptor {
+ style: UsedFontStyle,
+ selector: FontSelector,
+}
+
+
+// TODO(Issue #181): use deriving for trivial cmp::Eq implementations
+pub impl FontDescriptor : cmp::Eq {
+ pure fn eq(other: &FontDescriptor) -> bool {
+ self.style == other.style &&
+ self.selector == other.selector
+ }
+ pure fn ne(other: &FontDescriptor) -> bool { !self.eq(other) }
+}
+
+pub impl FontDescriptor {
+ static pure fn new(style: &UsedFontStyle, selector: &FontSelector) -> FontDescriptor {
+ FontDescriptor {
+ style: copy *style,
+ selector: copy *selector,
+ }
+ }
+}
+
+// A FontSelector is a platform-specific strategy for serializing face names.
+pub enum FontSelector {
+ SelectorPlatformName(~str),
+ SelectorStubDummy, // aka, use Josephin Sans
+}
+
+// TODO(Issue #181): use deriving for trivial cmp::Eq implementations
+pub impl FontSelector : cmp::Eq {
+ pure fn eq(other: &FontSelector) -> bool {
+ match (&self, other) {
+ (&SelectorStubDummy, &SelectorStubDummy) => true,
+ (&SelectorPlatformName(a), &SelectorPlatformName(b)) => a == b,
+ _ => false
+ }
+ }
+ pure fn ne(other: &FontSelector) -> bool { !self.eq(other) }
+}
+
+// This struct is the result of mapping a specified FontStyle into the
+// available fonts on the system. It contains an ordered list of font
+// instances to be used in case the prior font cannot be used for
+// rendering the specified language.
+
+// The ordering of font instances is mainly decided by the CSS
+// 'font-family' property. The last font is a system fallback font.
+pub struct FontGroup {
+ families: @str,
+ // style of the first western font in group, which is
+ // used for purposes of calculating text run metrics.
+ style: UsedFontStyle,
+ fonts: ~[@Font],
+}
+
+pub impl FontGroup {
+ static fn new(families: @str, style: &UsedFontStyle, fonts: ~[@Font]) -> FontGroup {
+ FontGroup {
+ families: families,
+ style: copy *style,
+ fonts: move fonts,
+ }
+ }
+}
+
+struct RunMetrics {
+ // may be negative due to negative width (i.e., kerning of '.' in 'P.T.')
+ advance_width: Au,
+ ascent: Au, // nonzero
+ descent: Au, // nonzero
+ // this bounding box is relative to the left origin baseline.
+ // so, bounding_box.position.y = -ascent
+ bounding_box: Rect<Au>
+}
+
+/**
+A font instance. Layout can use this to calculate glyph metrics
+and the renderer can use it to render text.
+*/
+pub struct Font {
+ priv fontbuf: @~[u8],
+ priv handle: FontHandle,
+ priv mut azure_font: Option<AzScaledFontRef>,
+ priv mut shaper: Option<@Shaper>,
+ style: UsedFontStyle,
+ metrics: FontMetrics,
+ backend: BackendType,
+
+ drop {
+ use azure::bindgen::AzReleaseScaledFont;
+ do (copy self.azure_font).iter |fontref| { AzReleaseScaledFont(*fontref); }
+ }
+}
+
+impl Font {
+ // TODO: who should own fontbuf?
+ static fn new(fontbuf: @~[u8],
+ handle: FontHandle,
+ style: UsedFontStyle,
+ backend: BackendType) -> Font {
+ let metrics = handle.get_metrics();
+
+ Font {
+ fontbuf : fontbuf,
+ handle : move handle,
+ azure_font: None,
+ shaper: None,
+ style: move style,
+ metrics: move metrics,
+ backend: backend
+ }
+ }
+
+ priv fn get_shaper(@self) -> @Shaper {
+ // fast path: already created a shaper
+ match self.shaper {
+ Some(shaper) => { return shaper; },
+ None => {}
+ }
+
+ let shaper = @Shaper::new(self);
+ self.shaper = Some(shaper);
+ shaper
+ }
+
+ priv fn get_azure_font() -> AzScaledFontRef {
+ // fast path: we've already created the azure font resource
+ match self.azure_font {
+ Some(azfont) => return azfont,
+ None => {}
+ }
+
+ let ct_font = &self.handle.ctfont;
+ let size = self.style.pt_size as AzFloat;
+ let scaled_font = azure::scaled_font::ScaledFont::new(self.backend, ct_font, size);
+
+ let azure_scaled_font;
+ unsafe {
+ azure_scaled_font = scaled_font.azure_scaled_font;
+ cast::forget(move scaled_font);
+ }
+
+ self.azure_font = Some(azure_scaled_font);
+ azure_scaled_font
+ /*
+ // TODO: these cairo-related things should be in rust-cairo.
+ // creating a cairo font/face from a native font resource
+ // should be part of the NativeFont API, not exposed here.
+ #[cfg(target_os = "linux")]
+ fn get_cairo_face(font: &Font) -> *cairo_font_face_t {
+ use cairo::cairo_ft::bindgen::{cairo_ft_font_face_create_for_ft_face};
+
+ let ftface = font.handle.face;
+ let cface = cairo_ft_font_face_create_for_ft_face(ftface, 0 as c_int);
+ // FIXME: error handling
+ return cface;
+ }
+ */
+ }
+}
+
+// Public API
+pub trait FontMethods {
+ fn draw_text_into_context(rctx: &RenderContext,
+ run: &TextRun,
+ range: Range,
+ baseline_origin: Point2D<Au>,
+ color: Color);
+ fn measure_text(&TextRun, Range) -> RunMetrics;
+ fn shape_text(@self, &str) -> GlyphStore;
+ fn get_descriptor() -> FontDescriptor;
+
+ fn buf(&self) -> @~[u8];
+ // these are used to get glyphs and advances in the case that the
+ // shaper can't figure it out.
+ fn glyph_index(char) -> Option<GlyphIndex>;
+ fn glyph_h_advance(GlyphIndex) -> FractionalPixel;
+}
+
+pub impl Font : FontMethods {
+ fn draw_text_into_context(rctx: &RenderContext,
+ run: &TextRun,
+ range: Range,
+ baseline_origin: Point2D<Au>,
+ color: Color) {
+ use libc::types::common::c99::{uint16_t, uint32_t};
+ use azure::{AzDrawOptions,
+ AzGlyph,
+ AzGlyphBuffer};
+ use azure::bindgen::{AzCreateColorPattern,
+ AzDrawTargetFillGlyphs,
+ AzReleaseColorPattern};
+
+ let target = rctx.get_draw_target();
+ let azfont = self.get_azure_font();
+ let pattern = ColorPattern(color);
+ let azure_pattern = pattern.azure_color_pattern;
+ assert azure_pattern.is_not_null();
+
+ let options: AzDrawOptions = {
+ mAlpha: 1f as AzFloat,
+ fields: 0x0200 as uint16_t
+ };
+
+ let mut origin = copy baseline_origin;
+ let azglyphs = DVec();
+ azglyphs.reserve(range.length());
+
+ for run.glyphs.iter_glyphs_for_range(range) |_i, glyph| {
+ let glyph_advance = glyph.advance();
+ let glyph_offset = glyph.offset().get_default(Au::zero_point());
+
+ let azglyph: AzGlyph = {
+ mIndex: glyph.index() as uint32_t,
+ mPosition: {
+ x: (origin.x + glyph_offset.x).to_px() as AzFloat,
+ y: (origin.y + glyph_offset.y).to_px() as AzFloat
+ }
+ };
+ origin = Point2D(origin.x + glyph_advance, origin.y);
+ azglyphs.push(move azglyph)
+ };
+
+ let azglyph_buf_len = azglyphs.len();
+ let azglyph_buf = dvec::unwrap(move azglyphs);
+ let glyphbuf: AzGlyphBuffer = unsafe {{
+ mGlyphs: vec::raw::to_ptr(azglyph_buf),
+ mNumGlyphs: azglyph_buf_len as uint32_t
+ }};
+
+ // TODO: this call needs to move into azure_hl.rs
+ AzDrawTargetFillGlyphs(target.azure_draw_target,
+ azfont,
+ ptr::to_unsafe_ptr(&glyphbuf),
+ azure_pattern,
+ ptr::to_unsafe_ptr(&options),
+ ptr::null());
+ }
+
+ fn measure_text(run: &TextRun, range: Range) -> RunMetrics {
+ assert range.is_valid_for_string(run.text);
+
+ debug!("measuring text range '%s'", run.text.substr(range.begin(), range.length()));
+
+ // TODO: alter advance direction for RTL
+ // TODO(Issue #98): using inter-char and inter-word spacing settings when measuring text
+ let mut advance = Au(0);
+ for run.glyphs.iter_glyphs_for_range(range) |_i, glyph| {
+ advance += glyph.advance();
+ }
+ let mut bounds = Rect(Point2D(Au(0), -self.metrics.ascent),
+ Size2D(advance, self.metrics.ascent + self.metrics.descent));
+
+ // TODO(Issue #125): support loose and tight bounding boxes; using the
+ // ascent+descent and advance is sometimes too generous and
+ // looking at actual glyph extents can yield a tighter box.
+
+ let metrics = RunMetrics { advance_width: advance,
+ bounding_box: bounds,
+ ascent: self.metrics.ascent,
+ descent: self.metrics.descent,
+ };
+ debug!("Measured text range '%s' with metrics:", run.text.substr(range.begin(), range.length()));
+ debug!("%?", metrics);
+
+ return metrics;
+ }
+
+ fn shape_text(@self, text: &str) -> GlyphStore {
+ let store = GlyphStore(text.len());
+ let shaper = self.get_shaper();
+ shaper.shape_text(text, &store);
+ return move store;
+ }
+
+ fn get_descriptor() -> FontDescriptor {
+ // TODO(Issue #174): implement by-platform-name FontSelectors,
+ // probably by adding such an API to FontHandle.
+ FontDescriptor::new(&font_context::dummy_style(), &SelectorStubDummy)
+ }
+
+ fn buf(&self) -> @~[u8] {
+ self.fontbuf
+ }
+
+ fn glyph_index(codepoint: char) -> Option<GlyphIndex> {
+ self.handle.glyph_index(codepoint)
+ }
+
+ fn glyph_h_advance(glyph: GlyphIndex) -> FractionalPixel {
+ match self.handle.glyph_h_advance(glyph) {
+ Some(adv) => adv,
+ None => /* FIXME: Need fallback strategy */ 10f as FractionalPixel
+ }
+ }
+}
+
+/*fn should_destruct_on_fail_without_leaking() {
+ #[test];
+ #[should_fail];
+
+ let fctx = @FontContext();
+ let matcher = @FontMatcher(fctx);
+ let _font = matcher.get_test_font();
+ fail;
+}
+
+fn should_get_glyph_indexes() {
+ #[test];
+
+ let fctx = @FontContext();
+ let matcher = @FontMatcher(fctx);
+ let font = matcher.get_test_font();
+ let glyph_idx = font.glyph_index('w');
+ assert glyph_idx == Some(40u as GlyphIndex);
+}
+
+fn should_get_glyph_advance() {
+ #[test];
+ #[ignore];
+
+ let fctx = @FontContext();
+ let matcher = @FontMatcher(fctx);
+ let font = matcher.get_test_font();
+ let x = font.glyph_h_advance(40u as GlyphIndex);
+ assert x == 15f || x == 16f;
+}
+
+// Testing thread safety
+fn should_get_glyph_advance_stress() {
+ #[test];
+ #[ignore];
+
+ let mut ports = ~[];
+
+ for iter::repeat(100) {
+ let (chan, port) = pipes::stream();
+ ports += [@move port];
+ do task::spawn |move chan| {
+ let fctx = @FontContext();
+ let matcher = @FontMatcher(fctx);
+ let _font = matcher.get_test_font();
+ let x = font.glyph_h_advance(40u as GlyphIndex);
+ assert x == 15f || x == 16f;
+ chan.send(());
+ }
+ }
+
+ for ports.each |port| {
+ port.recv();
+ }
+}
+
+fn should_be_able_to_create_instances_in_multiple_threads() {
+ #[test];
+
+ for iter::repeat(10u) {
+ do task::spawn {
+ let fctx = @FontContext();
+ let matcher = @FontMatcher(fctx);
+ let _font = matcher.get_test_font();
+ }
+ }
+}
+
+*/
View
124 src/servo-gfx-2/font_context.rs
@@ -0,0 +1,124 @@
+use font::{Font, FontDescriptor, FontGroup, FontStyle, SelectorPlatformName, SelectorStubDummy};
+use font::{SpecifiedFontStyle};
+use font_list::FontList;
+use native::FontHandle;
+use util::cache;
+
+use azure::azure_hl::BackendType;
+use core::dvec::DVec;
+
+// TODO(Issue #164): delete, and get default font from font list
+const TEST_FONT: [u8 * 33004] = #include_bin("JosefinSans-SemiBold.ttf");
+
+fn test_font_bin() -> ~[u8] {
+ return vec::from_fn(33004, |i| TEST_FONT[i]);
+}
+
+// TODO(Rust #3934): creating lots of new dummy styles is a workaround
+// for not being able to store symbolic enums in top-level constants.
+pub fn dummy_style() -> FontStyle {
+ use font::FontWeight300;
+ return FontStyle {
+ pt_size: 20f,
+ weight: FontWeight300,
+ italic: false,
+ oblique: false,
+ families: ~"Helvetica, serif",
+ }
+}
+
+// TODO(Issue #163): this is a workaround for static methods and
+// typedefs not working well together. It should be removed.
+#[cfg(target_os = "macos")]
+type FontContextHandle/& = quartz::font_context::QuartzFontContextHandle;
+
+#[cfg(target_os = "linux")]
+type FontContextHandle/& = freetype::font_context::FreeTypeFontContextHandle;
+
+pub impl FontContextHandle {
+ #[cfg(target_os = "macos")]
+ static pub fn new() -> FontContextHandle {
+ quartz::font_context::QuartzFontContextHandle::new()
+ }
+
+ #[cfg(target_os = "linux")]
+ static pub fn new() -> FontContextHandle {
+ freetype::font_context::FreeTypeFontContextHandle::new()
+ }
+}
+
+pub struct FontContext {
+ instance_cache: cache::MonoCache<FontDescriptor, @Font>,
+ font_list: Option<FontList>, // only needed by layout
+ handle: FontContextHandle,
+ backend: BackendType,
+}
+
+pub impl FontContext {
+ static fn new(backend: BackendType, needs_font_list: bool) -> FontContext {
+ let handle = FontContextHandle::new();
+ let font_list = if needs_font_list { Some(FontList::new(&handle)) } else { None };
+ FontContext {
+ // TODO(Rust #3902): remove extraneous type parameters once they are inferred correctly.
+ instance_cache: cache::new::<FontDescriptor, @Font, cache::MonoCache<FontDescriptor, @Font>>(10),
+ font_list: move font_list,
+ handle: move handle,
+ backend: backend
+ }
+ }
+
+ fn get_resolved_font_for_style(style: &SpecifiedFontStyle) -> @FontGroup {
+ // TODO(Issue #178, E): implement a cache of FontGroup instances.
+ self.create_font_group(style)
+ }
+
+ fn get_font_by_descriptor(desc: &FontDescriptor) -> Result<@Font, ()> {
+ match self.instance_cache.find(desc) {
+ Some(f) => Ok(f),
+ None => {
+ let result = self.create_font_instance(desc);
+ match result {
+ Ok(font) => {
+ self.instance_cache.insert(desc, font);
+ }, _ => {}
+ };
+ result
+ }
+ }
+ }
+
+ priv fn create_font_group(style: &SpecifiedFontStyle) -> @FontGroup {
+ // TODO(Issue #178, D): implement private font matching
+ // TODO(Issue #174): implement by-platform-name FontSelectors
+ let desc = FontDescriptor::new(&font_context::dummy_style(), &SelectorStubDummy);
+ let fonts = DVec();
+ match self.get_font_by_descriptor(&desc) {
+ Ok(instance) => fonts.push(instance),
+ Err(()) => {}
+ }
+
+ assert fonts.len() > 0;
+ // TODO(Issue #179): Split FontStyle into specified and used styles
+ let used_style = copy *style;
+
+ @FontGroup::new(style.families.to_managed(), &used_style, dvec::unwrap(move fonts))
+ }
+
+ priv fn create_font_instance(desc: &FontDescriptor) -> Result<@Font, ()> {
+ match desc.selector {
+ SelectorStubDummy => {
+ let font_bin = @test_font_bin();
+ let handle = FontHandle::new(&self.handle, font_bin, desc.style.pt_size);
+ let handle = if handle.is_ok() {
+ result::unwrap(move handle)
+ } else {
+ return Err(handle.get_err());
+ };
+
+ return Ok(@Font::new(font_bin, move handle, copy desc.style, self.backend));
+ },
+ // TODO(Issue #174): implement by-platform-name font selectors.
+ SelectorPlatformName(_) => { fail ~"FontContext::create_font_instance() can't yet handle SelectorPlatformName." }
+ }
+ }
+}
View
137 src/servo-gfx-2/font_list.rs
@@ -0,0 +1,137 @@
+use font::{CSSFontWeight, SpecifiedFontStyle};
+use native::FontHandle;
+
+use dvec::DVec;
+use send_map::{linear, SendMap};
+
+#[cfg(target_os = "macos")]
+type FontListHandle/& = quartz::font_list::QuartzFontListHandle;
+
+#[cfg(target_os = "linux")]
+type FontListHandle/& = freetype::font_list::FreeTypeFontListHandle;
+
+pub impl FontListHandle {
+ #[cfg(target_os = "macos")]
+ static pub fn new(fctx: &native::FontContextHandle) -> Result<FontListHandle, ()> {
+ Ok(quartz::font_list::QuartzFontListHandle::new(fctx))
+ }
+
+ #[cfg(target_os = "linux")]
+ static pub fn new(fctx: &native::FontContextHandle) -> Result<FontListHandle, ()> {
+ Ok(freetype::font_list::FreeTypeFontListHandle::new(fctx))
+ }
+}
+
+type FontFamilyMap = linear::LinearMap<~str, @FontFamily>;
+
+pub struct FontList {
+ mut family_map: FontFamilyMap,
+ mut handle: FontListHandle,
+}
+
+pub impl FontList {
+ static fn new(fctx: &native::FontContextHandle) -> FontList {
+ let handle = result::unwrap(FontListHandle::new(fctx));
+ let list = FontList {
+ handle: move handle,
+ family_map: linear::LinearMap(),
+ };
+ list.refresh(fctx);
+ return move list;
+ }
+
+ priv fn refresh(fctx: &native::FontContextHandle) {
+ // TODO(Issue #186): don't refresh unless something actually
+ // changed. Does OSX have a notification for this event?
+ do util::time::time("gfx::font_list: regenerating available font families and faces") {
+ self.family_map = self.handle.get_available_families(fctx);
+ }
+ }
+
+ fn find_font_in_family(family_name: &str,
+ style: &SpecifiedFontStyle) -> Option<@FontEntry> {
+ let family = self.find_family(family_name);
+ let mut result : Option<@FontEntry> = None;
+
+ // if such family exists, try to match style to a font
+ do family.iter |fam| {
+ result = fam.find_font_for_style(style);
+ }
+ return result;
+ }
+
+ priv fn find_family(family_name: &str) -> Option<@FontFamily> {
+ // look up canonical name
+ let family = self.family_map.find(&str::from_slice(family_name));
+
+ // TODO(Issue #188): look up localized font family names if canonical name not found
+ return family;
+ }
+}
+
+// Holds a specific font family, and the various
+pub struct FontFamily {
+ family_name: @str,
+ entries: DVec<@FontEntry>,
+}
+
+pub impl FontFamily {
+ static fn new(family_name: &str) -> FontFamily {
+ FontFamily {
+ family_name: str::from_slice(family_name).to_managed(),
+ entries: DVec(),
+ }
+ }
+
+ pure fn find_font_for_style(style: &SpecifiedFontStyle) -> Option<@FontEntry> {
+ assert self.entries.len() > 0;
+
+ // TODO(Issue #189): optimize lookup for
+ // regular/bold/italic/bolditalic with fixed offsets and a
+ // static decision table for fallback between these values.
+
+ // TODO(Issue #190): if not in the fast path above, do
+ // expensive matching of weights, etc.
+ for self.entries.each |entry| {
+ if (style.weight.is_bold() == entry.is_bold()) &&
+ (style.italic == entry.is_italic()) {
+
+ return Some(*entry);
+ }
+ }
+
+ return None;
+ }
+}
+
+// This struct summarizes an available font's features. In the future,
+// this will include fiddly settings such as special font table handling.
+
+// In the common case, each FontFamily will have a singleton FontEntry, or
+// it will have the standard four faces: Normal, Bold, Italic, BoldItalic.
+struct FontEntry {
+ family: @FontFamily,
+ face_name: ~str,
+ priv weight: CSSFontWeight,
+ priv italic: bool,
+ handle: FontHandle,
+ // TODO: array of OpenType features, etc.
+}
+
+impl FontEntry {
+ static fn new(family: @FontFamily, handle: FontHandle) -> FontEntry {
+ FontEntry {
+ family: family,
+ face_name: handle.face_name(),
+ weight: handle.boldness(),
+ italic: handle.is_italic(),
+ handle: move handle
+ }
+ }
+
+ pure fn is_bold() -> bool {
+ self.weight.is_bold()
+ }
+
+ pure fn is_italic() -> bool { self.italic }
+}
View
12 src/servo-gfx-2/fontconfig/font_list.rs
@@ -0,0 +1,12 @@
+pub struct FontconfigFontContextHandle {
+ ctx: (),
+
+ drop { }
+}
+
+pub impl FontconfigFontContextHandle {
+ // this is a placeholder.
+ static pub fn new() -> FontconfigFontContextHandle {
+ FontconfigFontContextHandle { ctx: () }
+ }
+}
View
151 src/servo-gfx-2/freetype/font.rs
@@ -0,0 +1,151 @@
+extern mod freetype;
+
+use font::{FontMetrics, FractionalPixel};
+use font_context::FreeTypeFontContext;
+use geometry::Au;
+use text::glyph::GlyphIndex;
+use text::util::{float_to_fixed, fixed_to_float};
+
+use cast::reinterpret_cast;
+use ptr::{addr_of, null};
+use vec_as_buf = vec::as_imm_buf;
+
+use freetype::{ FT_Error, FT_Library, FT_Face, FT_Long, FT_ULong, FT_Size, FT_SizeRec,
+ FT_UInt, FT_GlyphSlot, FT_Size_Metrics, FT_FaceRec, FT_F26Dot6 };
+use freetype::bindgen::{
+ FT_Init_FreeType,
+ FT_Done_FreeType,
+ FT_New_Memory_Face,
+ FT_Done_Face,
+ FT_Get_Char_Index,
+ FT_Load_Glyph,
+ FT_Set_Char_Size
+};
+
+fn float_to_fixed_ft(f: float) -> i32 {
+ float_to_fixed(6, f)
+}
+
+fn fixed_to_float_ft(f: i32) -> float {
+ fixed_to_float(6, f)
+}
+
+pub struct FreeTypeFontHandle {
+ /// The font binary. This must stay valid for the lifetime of the font
+ buf: @~[u8],
+ face: FT_Face,
+
+ drop {
+ assert self.face.is_not_null();
+ if !FT_Done_Face(self.face).succeeded() {
+ fail ~"FT_Done_Face failed";
+ }
+ }
+}
+
+pub impl FreeTypeFontHandle {
+ static pub fn new(fctx: &FreeTypeFontContext,
+ buf: @~[u8], pt_size: float) -> Result<FreeTypeFontHandle, ()> {
+ let ft_ctx = fctx.ctx;
+ assert ft_ctx.is_not_null();
+ let face: FT_Face = null();
+ return vec_as_buf(*buf, |cbuf, _len| {
+ if FT_New_Memory_Face(ft_ctx, cbuf, (*buf).len() as FT_Long,
+ 0 as FT_Long, addr_of(&face)).succeeded() {
+ let res = FT_Set_Char_Size(face, // the face
+ float_to_fixed_ft(pt_size) as FT_F26Dot6, // char width
+ float_to_fixed_ft(pt_size) as FT_F26Dot6, // char height
+ 72, // horiz. DPI
+ 72); // vert. DPI
+ if !res.succeeded() { fail ~"unable to set font char size" }
+ Ok(FreeTypeFontHandle { face: face, buf: buf })
+ } else {
+ Err(())
+ }
+ })
+ }
+
+ pub fn glyph_index(codepoint: char) -> Option<GlyphIndex> {
+ assert self.face.is_not_null();
+ let idx = FT_Get_Char_Index(self.face, codepoint as FT_ULong);
+ return if idx != 0 as FT_UInt {
+ Some(idx as GlyphIndex)
+ } else {
+ debug!("Invalid codepoint: %?", codepoint);
+ None
+ };
+ }
+
+ pub fn glyph_h_advance(glyph: GlyphIndex) -> Option<FractionalPixel> {
+ assert self.face.is_not_null();
+ let res = FT_Load_Glyph(self.face, glyph as FT_UInt, 0);
+ if res.succeeded() {
+ unsafe {
+ let void_glyph = (*self.face).glyph;
+ let slot: FT_GlyphSlot = reinterpret_cast(&void_glyph);
+ assert slot.is_not_null();
+ debug!("metrics: %?", (*slot).metrics);
+ let advance = (*slot).metrics.horiAdvance;
+ debug!("h_advance for %? is %?", glyph, advance);
+ let advance = advance as i32;
+ return Some(fixed_to_float_ft(advance) as FractionalPixel);
+ }
+ } else {
+ debug!("Unable to load glyph %?. reason: %?", glyph, res);
+ return None;
+ }
+ }
+
+ pub fn get_metrics() -> FontMetrics {
+ /* TODO(Issue #76): complete me */
+ let face = self.get_face_rec();
+
+ let underline_size = self.font_units_to_au(face.underline_thickness as float);
+ let underline_offset = self.font_units_to_au(face.underline_position as float);
+ let em_size = self.font_units_to_au(face.units_per_EM as float);
+ let ascent = self.font_units_to_au(face.ascender as float);
+ let descent = self.font_units_to_au(face.descender as float);
+ let max_advance = self.font_units_to_au(face.max_advance_width as float);
+
+ return FontMetrics {
+ underline_size: underline_size,
+ underline_offset: underline_offset,
+ leading: geometry::from_pt(0.0), //FIXME
+ x_height: geometry::from_pt(0.0), //FIXME
+ em_size: em_size,
+ ascent: ascent,
+ descent: descent,
+ max_advance: max_advance
+ }
+ }
+
+ priv fn get_face_rec() -> &self/FT_FaceRec unsafe {
+ &(*self.face)
+ }
+
+ priv fn font_units_to_au(value: float) -> Au {
+
+ let face = self.get_face_rec();
+
+ // face.size is a *c_void in the bindings, presumably to avoid
+ // recursive structural types
+ let size: &FT_SizeRec = unsafe { cast::transmute(&(*face.size)) };
+ let metrics: &FT_Size_Metrics = unsafe { &((*size).metrics) };
+
+ let em_size = face.units_per_EM as float;
+ let x_scale = (metrics.x_ppem as float) / em_size as float;
+
+ // If this isn't true then we're scaling one of the axes wrong
+ assert metrics.x_ppem == metrics.y_ppem;
+
+ return geometry::from_frac_px(value * x_scale);
+ }
+}
+
+trait FTErrorMethods {
+ fn succeeded() -> bool;
+}
+
+impl FT_Error : FTErrorMethods {
+ fn succeeded() -> bool { self == 0 as FT_Error }
+}
View
33 src/servo-gfx-2/freetype/font_context.rs
@@ -0,0 +1,33 @@
+extern mod freetype;
+
+use freetype::{
+ FT_Error,
+ FT_Library,
+};
+use freetype::bindgen::{
+ FT_Init_FreeType,
+ FT_Done_FreeType
+};
+
+
+pub struct FreeTypeFontContextHandle {
+ ctx: FT_Library,
+
+ drop {
+ assert self.ctx.is_not_null();
+ FT_Done_FreeType(self.ctx);
+ }
+}
+
+pub impl FreeTypeFontContextHandle {
+ static pub fn new() -> FreeTypeFontContextHandle {
+ let lib: FT_Library = ptr::null();
+ let res = FT_Init_FreeType(ptr::addr_of(&lib));
+ // FIXME: error handling
+ assert res == 0 as FT_Error;
+
+ FreeTypeFontContext {
+ ctx: lib,
+ }
+ }
+}
View
109 src/servo-gfx-2/geometry.rs
@@ -0,0 +1,109 @@
+use geom::point::Point2D;
+use geom::rect::Rect;
+use geom::size::Size2D;
+use num::{Num, from_int};
+
+pub enum Au = i32;
+
+impl Au : Num {
+ pure fn add(other: &Au) -> Au { Au(*self + **other) }
+ pure fn sub(other: &Au) -> Au { Au(*self - **other) }
+ pure fn mul(other: &Au) -> Au { Au(*self * **other) }
+ pure fn div(other: &Au) -> Au { Au(*self / **other) }
+ pure fn modulo(other: &Au) -> Au { Au(*self % **other) }
+ pure fn neg() -> Au { Au(-*self) }
+
+ pure fn to_int() -> int { *self as int }
+
+ static pure fn from_int(n: int) -> Au {
+ Au((n & (i32::max_value as int)) as i32)
+ }
+}
+
+impl Au : cmp::Ord {
+ pure fn lt(other: &Au) -> bool { *self < **other }
+ pure fn le(other: &Au) -> bool { *self <= **other }
+ pure fn ge(other: &Au) -> bool { *self >= **other }
+ pure fn gt(other: &Au) -> bool { *self > **other }
+}
+
+impl Au : cmp::Eq {
+ pure fn eq(other: &Au) -> bool { *self == **other }
+ pure fn ne(other: &Au) -> bool { *self != **other }
+}
+
+pub pure fn min(x: Au, y: Au) -> Au { if x < y { x } else { y } }
+pub pure fn max(x: Au, y: Au) -> Au { if x > y { x } else { y } }
+
+pub fn box<A:Copy Num>(x: A, y: A, w: A, h: A) -> Rect<A> {
+ Rect(Point2D(x, y), Size2D(w, h))
+}
+
+impl Au {
+ pub pure fn scale_by(factor: float) -> Au {
+ Au(((*self as float) * factor) as i32)
+ }
+
+ static pub pure fn from_px(i: int) -> Au {
+ from_int(i * 60)
+ }
+
+ pub pure fn to_px(&const self) -> int {
+ (**self / 60) as int
+ }
+
+ static pub pure fn zero_point() -> Point2D<Au> {
+ Point2D(Au(0), Au(0))
+ }
+
+ static pub pure fn zero_rect() -> Rect<Au> {
+ let z = Au(0);
+ Rect(Point2D(z, z), Size2D(z, z))
+ }
+
+ // assumes 72 points per inch, and 96 px per inch
+ static pub pure fn from_pt(f: float) -> Au {
+ from_px((f / 72f * 96f) as int)
+ }
+
+ static pub pure fn from_frac_px(f: float) -> Au {
+ Au((f * 60f) as i32)
+ }
+
+ static pub pure fn min(x: Au, y: Au) -> Au { if *x < *y { x } else { y } }
+ static pub pure fn max(x: Au, y: Au) -> Au { if *x > *y { x } else { y } }
+}
+
+pub pure fn zero_rect() -> Rect<Au> {
+ let z = Au(0);
+ Rect(Point2D(z, z), Size2D(z, z))
+}
+
+pub pure fn zero_point() -> Point2D<Au> {
+ Point2D(Au(0), Au(0))
+}
+
+pub pure fn zero_size() -> Size2D<Au> {
+ Size2D(Au(0), Au(0))
+}
+
+pub pure fn from_frac_px(f: float) -> Au {
+ Au((f * 60f) as i32)
+}
+
+pub pure fn from_px(i: int) -> Au {
+ from_int(i * 60)
+}
+
+pub pure fn to_px(au: Au) -> int {
+ (*au / 60) as int
+}
+
+pub pure fn to_frac_px(au: Au) -> float {
+ (*au as float) / 60f
+}
+
+// assumes 72 points per inch, and 96 px per inch
+pub pure fn from_pt(f: float) -> Au {
+ from_px((f / 72f * 96f) as int)
+}
View
43 src/servo-gfx-2/image/base.rs
@@ -0,0 +1,43 @@
+use stb_image = stb_image::image;
+
+// FIXME: Images must not be copied every frame. Instead we should atomically
+// reference count them.
+
+pub type Image = stb_image::Image;
+
+pub fn Image(width: uint, height: uint, depth: uint, data: ~[u8]) -> Image {
+ stb_image::new_image(width, height, depth, move data)
+}
+
+const TEST_IMAGE: [u8 * 4962] = #include_bin("test.jpeg");
+
+pub fn test_image_bin() -> ~[u8] {
+ return vec::from_fn(4962, |i| TEST_IMAGE[i]);
+}
+
+pub fn load_from_memory(buffer: &[u8]) -> Option<Image> {
+
+ // Can't remember why we do this. Maybe it's what cairo wants
+ const FORCE_DEPTH: uint = 4;
+
+ do stb_image::load_from_memory_with_depth(buffer, FORCE_DEPTH).map |image| {
+
+ assert image.depth == 4;
+ // Do color space conversion :(
+ let data = do vec::from_fn(image.width * image.height * 4) |i| {
+ let color = i % 4;
+ let pixel = i / 4;
+ match color {
+ 0 => image.data[pixel * 4 + 2],
+ 1 => image.data[pixel * 4 + 1],
+ 2 => image.data[pixel * 4 + 0],
+ 3 => 0xffu8,
+ _ => fail
+ }
+ };
+
+ assert image.data.len() == data.len();
+
+ Image(image.width, image.height, image.depth, move data)
+ }
+}
View
23 src/servo-gfx-2/image/encode/tga.rs
@@ -0,0 +1,23 @@
+use io::WriterUtil;
+
+fn encode(writer: io::Writer, surface: &surface::image_surface) {
+ assert surface.format == surface::fo_rgba_8888;
+
+ writer.write_u8(0u8); // identsize
+ writer.write_u8(0u8); // colourmaptype
+ writer.write_u8(2u8); // imagetype
+
+ writer.write_le_u16(0u16); // colourmapstart
+ writer.write_le_u16(0u16); // colourmaplength
+ writer.write_u8(16u8); // colourmapbits
+
+ writer.write_le_u16(0u16); // xstart
+ writer.write_le_u16(0u16); // ystart
+ writer.write_le_u16(surface.size.width as u16); // width
+ writer.write_le_u16(surface.size.height as u16); // height
+ writer.write_u8(32u8); // bits
+ writer.write_u8(0x30u8); // descriptor
+
+ writer.write(surface.buffer);
+}
+
View
102 src/servo-gfx-2/image/holder.rs
@@ -0,0 +1,102 @@
+use image::base::Image;
+use resource::image_cache_task::{ImageCacheTask, ImageReady, ImageNotReady, ImageFailed};
+use resource::image_cache_task;
+use resource::local_image_cache::LocalImageCache;
+
+use core::util::replace;
+use geom::size::Size2D;
+use std::net::url::Url;
+use std::arc::{ARC, clone, get};
+
+// FIXME: Nasty coupling to resource here. This should probably be factored out into an interface
+// and use dependency injection.
+
+/** A struct to store image data. The image will be loaded once, the
+ first time it is requested, and an arc will be stored. Clones of
+ this arc are given out on demand.
+ */
+pub struct ImageHolder {
+ url: Url,
+ mut image: Option<ARC<~Image>>,
+ mut cached_size: Size2D<int>,
+ local_image_cache: @LocalImageCache,
+}
+
+impl ImageHolder {
+ static pub fn new(url: Url, local_image_cache: @LocalImageCache) -> ImageHolder {
+ debug!("ImageHolder::new() %?", url.to_str());
+ let holder = ImageHolder {
+ url: move url,
+ image: None,
+ cached_size: Size2D(0,0),
+ local_image_cache: local_image_cache,
+ };
+
+ // Tell the image cache we're going to be interested in this url
+ // FIXME: These two messages must be sent to prep an image for use
+ // but they are intended to be spread out in time. Ideally prefetch
+ // should be done as early as possible and decode only once we
+ // are sure that the image will be used.
+ local_image_cache.prefetch(&holder.url);
+ local_image_cache.decode(&holder.url);
+
+ move holder
+ }
+
+ /**
+ This version doesn't perform any computation, but may be stale w.r.t.
+ newly-available image data that determines size.
+
+ The intent is that the impure version is used during layout when
+ dimensions are used for computing layout.
+ */
+ pure fn size() -> Size2D<int> {
+ self.cached_size
+ }
+
+ /** Query and update current image size */
+ fn get_size() -> Option<Size2D<int>> {
+ debug!("get_size() %?", self.url);
+ match self.get_image() {
+ Some(img) => {
+ let img_ref = get(&img);
+ self.cached_size = Size2D(img_ref.width as int,
+ img_ref.height as int);
+ Some(copy self.cached_size)
+ },
+ None => None
+ }
+ }
+
+ fn get_image() -> Option<ARC<~Image>> {
+ debug!("get_image() %?", self.url);
+
+ // If this is the first time we've called this function, load
+ // the image and store it for the future
+ if self.image.is_none() {
+ match self.local_image_cache.get_image(&self.url).recv() {
+ ImageReady(move image) => {
+ self.image = Some(move image);
+ }
+ ImageNotReady => {
+ debug!("image not ready for %s", self.url.to_str());
+ }
+ ImageFailed => {
+ debug!("image decoding failed for %s", self.url.to_str());
+ }
+ }
+ }
+
+ // Clone isn't pure so we have to swap out the mutable image option
+ let image = replace(&mut self.image, None);
+
+ let result = match image {
+ Some(ref image) => Some(clone(image)),
+ None => None
+ };
+
+ replace(&mut self.image, move image);
+
+ return move result;
+ }
+}
View
BIN  src/servo-gfx-2/image/test.jpeg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
9 src/servo-gfx-2/native.rs
@@ -0,0 +1,9 @@
+/* This file exists just to make it easier to import platform-specific
+ implementations.
+
+Note that you still must define each of the files as a module in
+servo_gfx.rc. This is not ideal and may be changed in the future. */
+
+pub use font::FontHandle;
+pub use font_context::FontContextHandle;
+pub use font_list::FontListHandle;
View
77 src/servo-gfx-2/opts.rs
@@ -0,0 +1,77 @@
+//! Configuration options for a single run of the servo application. Created
+//! from command line arguments.
+
+use azure::azure_hl::{BackendType, CairoBackend, CoreGraphicsBackend};
+use azure::azure_hl::{CoreGraphicsAcceleratedBackend, Direct2DBackend, SkiaBackend};
+
+pub struct Opts {
+ urls: ~[~str],
+ render_mode: RenderMode,
+ render_backend: BackendType,
+ n_render_threads: uint,
+}
+
+pub enum RenderMode {
+ Screen,
+ Png(~str)
+}
+
+#[allow(non_implicitly_copyable_typarams)]
+pub fn from_cmdline_args(args: &[~str]) -> Opts {
+ use std::getopts;
+
+ let args = args.tail();
+
+ let opts = ~[
+ getopts::optopt(~"o"),
+ getopts::optopt(~"r"),
+ getopts::optopt(~"t"),
+ ];
+
+ let opt_match = match getopts::getopts(args, opts) {
+ result::Ok(m) => { copy m }
+ result::Err(f) => { fail getopts::fail_str(copy f) }
+ };
+
+ let urls = if opt_match.free.is_empty() {
+ fail ~"servo asks that you provide 1 or more URLs"
+ } else {
+ copy opt_match.free
+ };
+
+ let render_mode = match getopts::opt_maybe_str(copy opt_match, ~"o") {
+ Some(move output_file) => { Png(move output_file) }
+ None => { Screen }
+ };
+
+ let render_backend = match getopts::opt_maybe_str(copy opt_match, ~"r") {
+ Some(move backend_str) => {
+ if backend_str == ~"direct2d" {
+ Direct2DBackend
+ } else if backend_str == ~"core-graphics" {
+ CoreGraphicsBackend
+ } else if backend_str == ~"core-graphics-accelerated" {
+ CoreGraphicsAcceleratedBackend
+ } else if backend_str == ~"cairo" {
+ CairoBackend
+ } else if backend_str == ~"skia" {
+ SkiaBackend
+ } else {
+ fail ~"unknown backend type"
+ }
+ }
+ None => CairoBackend
+ };
+
+ let n_render_threads: uint = match getopts::opt_maybe_str(move opt_match, ~"t") {
+ Some(move n_render_threads_str) => from_str::from_str(n_render_threads_str).get(),
+ None => 1, // FIXME: Number of cores.
+ };
+
+ Opts {
+ urls: move urls,
+ render_mode: move render_mode,
+ render_backend: move render_backend,
+ n_render_threads: n_render_threads,
+ }
+}
View
161 src/servo-gfx-2/quartz/font.rs
@@ -0,0 +1,161 @@
+extern mod core_foundation;
+extern mod core_graphics;
+extern mod core_text;
+
+use font_context::QuartzFontContextHandle;
+use geometry::Au;
+use servo_gfx_font::{CSSFontWeight, FontHandleMethods, FontMetrics, FontWeight100, FontWeight200};
+use servo_gfx_font::{FontWeight300, FontWeight400, FontWeight500, FontWeight600, FontWeight700};
+use servo_gfx_font::{FontWeight800, FontWeight900, FractionalPixel};
+use text::glyph::GlyphIndex;
+
+use cf = core_foundation;
+use cf::base::{CFIndex, CFRelease, CFTypeRef};
+use cf::string::UniChar;
+use cg = core_graphics;
+use cg::base::{CGFloat, CGAffineTransform};
+use cg::data_provider::{CGDataProviderRef, CGDataProvider};
+use cg::font::{CGFontCreateWithDataProvider, CGFontRef, CGFontRelease, CGGlyph};
+use cg::geometry::CGRect;
+use core::libc::size_t;
+use ct = core_text;
+use ct::font::CTFont;
+use ct::font_descriptor::{kCTFontDefaultOrientation, CTFontSymbolicTraits};
+use ct::font_descriptor::{SymbolicTraitAccessors};
+
+pub struct QuartzFontHandle {
+ priv mut cgfont: Option<CGFontRef>,
+ ctfont: CTFont,
+
+ drop {
+ // TODO(Issue #152): use a wrapped CGFont.
+ do (copy self.cgfont).iter |cgfont| {
+ assert cgfont.is_not_null();
+ CGFontRelease(*cgfont);
+ }
+ }
+}
+
+pub impl QuartzFontHandle {
+ static fn new_from_buffer(_fctx: &QuartzFontContextHandle, buf: @~[u8], pt_size: float) -> Result<QuartzFontHandle, ()> {
+ let fontprov = vec::as_imm_buf(*buf, |cbuf, len| {
+ CGDataProvider::new_from_buffer(cbuf, len)
+ });
+
+ let cgfont = CGFontCreateWithDataProvider(fontprov.get_ref());
+ if cgfont.is_null() { return Err(()); }
+
+ let ctfont = CTFont::new_from_CGFont(cgfont, pt_size);
+
+ let result = Ok(QuartzFontHandle {
+ cgfont : Some(cgfont),
+ ctfont : move ctfont,
+ });
+
+ return move result;
+ }
+
+ static fn new_from_CTFont(_fctx: &QuartzFontContextHandle, ctfont: CTFont) -> Result<QuartzFontHandle, ()> {
+ let result = Ok(QuartzFontHandle {
+ mut cgfont: None,
+ ctfont: move ctfont,
+ });
+
+ return move result;
+ }
+
+ fn get_CGFont() -> CGFontRef {
+ match self.cgfont {
+ Some(cg) => cg,
+ None => {
+ let cgfont = self.ctfont.copy_to_CGFont();
+ self.cgfont = Some(cgfont);
+ cgfont
+ }
+ }
+ }
+}
+
+pub impl QuartzFontHandle : FontHandleMethods {
+ pure fn family_name() -> ~str {
+ self.ctfont.family_name()
+ }
+
+ pure fn face_name() -> ~str {
+ self.ctfont.face_name()
+ }
+
+ pure fn is_italic() -> bool {
+ self.ctfont.symbolic_traits().is_italic()
+ }
+
+ pure fn boldness() -> CSSFontWeight {
+ // -1.0 to 1.0
+ let normalized = unsafe { self.ctfont.all_traits().normalized_weight() };
+ // 0.0 to 9.0
+ let normalized = (normalized + 1.0) / 2.0 * 9.0;
+ if normalized < 1.0 { return FontWeight100; }
+ if normalized < 2.0 { return FontWeight200; }
+ if normalized < 3.0 { return FontWeight300; }
+ if normalized < 4.0 { return FontWeight400; }
+ if normalized < 5.0 { return FontWeight500; }
+ if normalized < 6.0 { return FontWeight600; }
+ if normalized < 7.0 { return FontWeight700; }
+ if normalized < 8.0 { return FontWeight800; }
+ else { return FontWeight900; }
+ }
+
+
+ fn glyph_index(codepoint: char) -> Option<GlyphIndex> {
+ let characters: ~[UniChar] = ~[codepoint as UniChar];
+ let glyphs: ~[mut CGGlyph] = ~[mut 0 as CGGlyph];
+ let count: CFIndex = 1;
+
+ let result = do vec::as_imm_buf(characters) |character_buf, _l| {
+ do vec::as_imm_buf(glyphs) |glyph_buf, _l| {
+ self.ctfont.get_glyphs_for_characters(character_buf, glyph_buf, count)
+ }
+ };
+
+ if !result {
+ // No glyph for this character
+ return None;
+ }
+
+ assert glyphs[0] != 0; // FIXME: error handling
+ return Some(glyphs[0] as GlyphIndex);
+ }
+
+ fn glyph_h_advance(glyph: GlyphIndex) -> Option<FractionalPixel> {
+ let glyphs = ~[glyph as CGGlyph];
+ let advance = do vec::as_imm_buf(glyphs) |glyph_buf, _l| {
+ self.ctfont.get_advances_for_glyphs(kCTFontDefaultOrientation, glyph_buf, ptr::null(), 1)
+ };
+
+ return Some(advance as FractionalPixel);
+ }
+
+ fn get_metrics() -> FontMetrics {
+ let bounding_rect: CGRect = self.ctfont.bounding_box();
+ let ascent = Au::from_pt(self.ctfont.ascent() as float);
+ let descent = Au::from_pt(self.ctfont.descent() as float);
+
+ let metrics = FontMetrics {
+ underline_size: Au::from_pt(self.ctfont.underline_thickness() as float),
+ // TODO: underline metrics are not reliable. Have to pull out of font table directly.
+ // see also: https://bugs.webkit.org/show_bug.cgi?id=16768
+ // see also: https://bugreports.qt-project.org/browse/QTBUG-13364
+ underline_offset: Au::from_pt(self.ctfont.underline_position() as float),
+ leading: Au::from_pt(self.ctfont.leading() as float),
+ x_height: Au::from_pt(self.ctfont.x_height() as float),
+ em_size: ascent + descent,
+ ascent: ascent,
+ descent: descent,
+ max_advance: Au::from_pt(bounding_rect.size.width as float)
+ };
+
+ debug!("Font metrics (@%f pt): %?", self.ctfont.pt_size() as float, metrics);
+ return metrics;
+ }
+}
+
View
12 src/servo-gfx-2/quartz/font_context.rs
@@ -0,0 +1,12 @@
+pub struct QuartzFontContextHandle {
+ ctx: (),
+
+ drop { }
+}
+
+pub impl QuartzFontContextHandle {
+ // this is a placeholder until NSFontManager or whatever is bound in here.
+ static pub fn new() -> QuartzFontContextHandle {
+ QuartzFontContextHandle { ctx: () }
+ }
+}
View
56 src/servo-gfx-2/quartz/font_list.rs
@@ -0,0 +1,56 @@
+extern mod core_foundation;
+extern mod core_text;
+
+use quartz::font::{QuartzFontHandle};
+use servo_gfx_font::FontHandle;
+use servo_gfx_font_list::{FontEntry, FontFamily};
+
+use cf = core_foundation;
+use cf::array::CFArray;
+use core::dvec::DVec;
+use core::send_map::{linear, SendMap};
+use ct = core_text;
+use ct::font::{CTFont, debug_font_names, debug_font_traits};
+use ct::font_collection::CTFontCollection;
+use ct::font_descriptor::{CTFontDescriptor, CTFontDescriptorRef, debug_descriptor};
+
+pub struct QuartzFontListHandle {
+ collection: CTFontCollection,
+}
+
+pub impl QuartzFontListHandle {
+ static pub fn new(_fctx: &native::FontContextHandle) -> QuartzFontListHandle {
+ QuartzFontListHandle { collection: CTFontCollection::new() }
+ }
+
+ fn get_available_families(&const self,
+ fctx: &native::FontContextHandle)
+ -> linear::LinearMap<~str, @FontFamily> {
+ // since we mutate it repeatedly, must be mut variable.
+ let mut family_map : linear::LinearMap<~str, @FontFamily> = linear::LinearMap();
+ let descriptors : CFArray<CTFontDescriptorRef, CTFontDescriptor>;
+ descriptors = self.collection.get_descriptors();
+ for descriptors.each |desc: &CTFontDescriptor| {
+ // TODO: for each descriptor, make a FontEntry.
+ let font = CTFont::new_from_descriptor(desc, 0.0);
+ let handle = result::unwrap(QuartzFontHandle::new_from_CTFont(fctx, move font));
+ let family_name = handle.family_name();
+ debug!("Looking for family name: %s", family_name);
+ let family = match family_map.find(&family_name) {
+ Some(fam) => fam,
+ None => {
+ debug!("Creating new FontFamily for family: %s", family_name);
+ let new_family = @FontFamily::new(family_name);
+ family_map.insert(move family_name, new_family);
+ new_family
+ }
+ };
+
+ debug!("Creating new FontEntry for face: %s", handle.face_name());
+ let entry = @FontEntry::new(family, move handle);
+ family.entries.push(entry);
+ // TODO: append FontEntry to hashtable value
+ }
+ return move family_map;
+ }
+}
View
105 src/servo-gfx-2/render_context.rs
@@ -0,0 +1,105 @@
+use compositor::LayerBuffer;
+use font_context::FontContext;
+use geometry::Au;
+use image::base::Image;
+use opts::Opts;
+use text::TextRun;
+use util::range::Range;
+
+use azure::azure_hl::{AsAzureRect, B8G8R8A8, Color, ColorPattern, DrawOptions};
+use azure::azure_hl::{DrawSurfaceOptions, DrawTarget, Linear, StrokeOptions};
+use azure::{AzDrawOptions, AzFloat};
+use core::dvec::DVec;
+use core::libc::types::common::c99::uint16_t;
+use core::ptr::to_unsafe_ptr;
+use geom::point::Point2D;
+use geom::rect::Rect;
+use geom::size::Size2D;
+use std::arc::ARC;
+
+struct RenderContext {
+ canvas: &LayerBuffer,
+ font_ctx: @FontContext,
+ opts: &Opts
+}
+
+impl RenderContext {
+ pub fn get_draw_target(&self) -> &self/DrawTarget {
+ &self.canvas.draw_target
+ }
+
+ pub fn draw_solid_color(&self, bounds: &Rect<Au>, color: Color) {
+ self.canvas.draw_target.fill_rect(&bounds.to_azure_rect(), &ColorPattern(color));
+ }
+
+ pub fn draw_border(&self, bounds: &Rect<Au>, width: Au, color: Color) {
+ let pattern = ColorPattern(color);
+ let stroke_fields = 2; // CAP_SQUARE
+ let width_px = width.to_px();
+ let rect = if width_px % 2 == 0 {
+ bounds.to_azure_rect()
+ } else {
+ bounds.to_azure_snapped_rect()
+ };
+
+ let stroke_opts = StrokeOptions(width_px as AzFloat, 10 as AzFloat, stroke_fields);
+ let draw_opts = DrawOptions(1 as AzFloat, 0 as uint16_t);
+
+ self.canvas.draw_target.stroke_rect(&rect, &pattern, &stroke_opts, &draw_opts);
+ }
+
+ pub fn draw_image(&self, bounds: Rect<Au>, image: ARC<~Image>) {
+ let image = std::arc::get(&image);
+ let size = Size2D(image.width as i32, image.height as i32);
+ let stride = image.width * 4;
+
+ let draw_target_ref = &self.canvas.draw_target;
+ let azure_surface = draw_target_ref.create_source_surface_from_data(image.data, size,
+ stride as i32, B8G8R8A8);
+ let source_rect = Rect(Point2D(0 as AzFloat, 0 as AzFloat),
+ Size2D(image.width as AzFloat, image.height as AzFloat));
+ let dest_rect = bounds.to_azure_rect();
+ let draw_surface_options = DrawSurfaceOptions(Linear, true);
+ let draw_options = DrawOptions(1.0f as AzFloat, 0);
+ draw_target_ref.draw_surface(move azure_surface, dest_rect, source_rect,
+ draw_surface_options, draw_options);
+ }
+
+ fn clear(&self) {
+ let pattern = ColorPattern(Color(1f as AzFloat, 1f as AzFloat, 1f as AzFloat, 1f as AzFloat));
+ let rect = Rect(Point2D(self.canvas.rect.origin.x as AzFloat,
+ self.canvas.rect.origin.y as AzFloat),
+ Size2D(self.canvas.rect.size.width as AzFloat,
+ self.canvas.rect.size.height as AzFloat));
+ self.canvas.draw_target.fill_rect(&rect, &pattern);
+ }
+}
+
+trait to_float {
+ fn to_float() -> float;
+}
+
+impl u8 : to_float {
+ fn to_float() -> float {
+ (self as float) / 255f
+ }
+}
+
+trait ToAzureRect {
+ fn to_azure_rect() -> Rect<AzFloat>;
+ fn to_azure_snapped_rect() -> Rect<AzFloat>;
+}
+
+impl Rect<Au> : ToAzureRect {
+ fn to_azure_rect() -> Rect<AzFloat> {
+ Rect(Point2D(self.origin.x.to_px() as AzFloat, self.origin.y.to_px() as AzFloat),
+ Size2D(self.size.width.to_px() as AzFloat, self.size.height.to_px() as AzFloat))
+ }
+
+ fn to_azure_snapped_rect() -> Rect<AzFloat> {
+ Rect(Point2D(self.origin.x.to_px() as AzFloat + 0.5f as AzFloat,
+ self.origin.y.to_px() as AzFloat + 0.5f as AzFloat),
+ Size2D(self.size.width.to_px() as AzFloat,
+ self.size.height.to_px() as AzFloat))
+ }
+}
View
137 src/servo-gfx-2/render_layers.rs
@@ -0,0 +1,137 @@
+use compositor::{LayerBuffer, LayerBufferSet};
+use display_list::DisplayList;
+use opts::Opts;
+use util::time;
+
+use azure::AzFloat;
+use azure::azure_hl::{B8G8R8A8, DrawTarget};
+use core::libc::c_int;
+use core::pipes::Chan;
+use geom::matrix2d::Matrix2D;
+use geom::point::Point2D;
+use geom::rect::Rect;
+use geom::size::Size2D;
+use std::arc::ARC;
+use std::arc;
+
+const TILE_SIZE: uint = 512;
+
+pub struct RenderLayer {
+ display_list: DisplayList,
+ size: Size2D<uint>
+}
+
+type RenderFn = &fn(layer: *RenderLayer,
+ buffer: LayerBuffer,
+ return_buffer: Chan<LayerBuffer>);
+
+/// Given a layer and a buffer, either reuses the buffer (if it's of the right size and format)
+/// or creates a new buffer (if it's not of the appropriate size and format) and invokes the
+/// given callback with the render layer and the buffer. Returns the resulting layer buffer (which
+/// might be the old layer buffer if it had the appropriate size and format).
+pub fn render_layers(layer_ref: *RenderLayer,
+ buffer_set: LayerBufferSet,
+ opts: &Opts,
+ f: RenderFn) -> LayerBufferSet {
+ let mut buffers = match move buffer_set { LayerBufferSet { buffers: move b } => move b };
+
+ // FIXME: Try not to create a new array here.
+ let new_buffer_ports = dvec::DVec();
+
+ // Divide up the layer into tiles.
+ do time::time("rendering: preparing buffers") {
+ let layer: &RenderLayer = unsafe { cast::transmute(layer_ref) };
+ let mut y = 0;
+ while y < layer.size.height {
+ let mut x = 0;
+ while x < layer.size.width {
+ // Figure out the dimension of this tile.
+ let right = uint::min(x + TILE_SIZE, layer.size.width);
+ let bottom = uint::min(y + TILE_SIZE, layer.size.height);
+ let width = right - x;
+ let height = bottom - y;
+
+ // Round the width up the nearest 32 pixels for DMA on the Mac.
+ let mut stride = width;
+ if stride % 32 != 0 {
+ stride = (stride & !(32 - 1)) + 32;
+ }
+ assert stride % 32 == 0;
+ assert stride >= width;
+
+ debug!("tile stride %u", stride);
+
+ let tile_rect = Rect(Point2D(x, y), Size2D(width, height));
+
+ let buffer;
+ // FIXME: Try harder to search for a matching tile.
+ // FIXME: Don't use shift; it's bad for perf. Maybe reverse and pop.
+ /*if buffers.len() != 0 && buffers[0].rect == tile_rect {
+ debug!("reusing tile, (%u, %u)", x, y);
+ buffer = buffers.shift();
+ } else {*/
+ // Create a new buffer.
+ debug!("creating tile, (%u, %u)", x, y);
+
+ let size = Size2D(stride as i32, height as i32);
+
+ let mut data: ~[u8] = ~[0];
+ let offset;
+ unsafe {
+ // FIXME: Evil black magic to ensure that we don't perform a slow memzero
+ // of this buffer. This should be made safe.
+
+ let align = 256;
+
+ let len = ((size.width * size.height * 4) as uint) + align;
+ vec::reserve(&mut data, len);
+ vec::raw::set_len(&mut data, len);
+
+ // Round up to the nearest 32-byte-aligned address for DMA on the Mac.
+ let addr: uint = cast::transmute(ptr::to_unsafe_ptr(&data[0]));
+ if addr % align == 0 {
+ offset = 0;
+ } else {
+ offset = align - addr % align;
+ }
+
+ debug!("tile offset is %u, expected addr is %x", offset, addr + offset);
+ }
+
+ buffer = LayerBuffer {
+ draw_target: DrawTarget::new_with_data(opts.render_backend,
+ move data,
+ offset,
+ size,
+ size.width * 4,
+ B8G8R8A8),
+ rect: tile_rect,
+ stride: stride
+ };
+ //}
+
+ // Create a port and channel pair to receive the new buffer.
+ let (new_buffer_chan, new_buffer_port) = pipes::stream();
+
+ // Send the buffer to the child.
+ f(layer_ref, move buffer, move new_buffer_chan);
+
+ // Enqueue the port.
+ new_buffer_ports.push(move new_buffer_port);
+
+ x += TILE_SIZE;
+ }
+ y += TILE_SIZE;
+ }
+ }
+
+ let new_buffers = dvec::DVec();
+ do time::time("rendering: waiting on subtasks") {
+ for new_buffer_ports.each |new_buffer_port| {
+ new_buffers.push(new_buffer_port.recv());
+ }
+ }
+
+ return LayerBufferSet { buffers: move dvec::unwrap(move new_buffers) };
+}
+
View
157 src/servo-gfx-2/render_task.rs
@@ -0,0 +1,157 @@
+use compositor::{Compositor, LayerBufferSet};
+use font_context::FontContext;
+use opts::Opts;
+use render_context::RenderContext;
+use render_layers::{RenderLayer, render_layers};
+
+use azure::AzFloat;
+use core::comm::*;
+use core::libc::size_t;
+use core::libc::types::common::c99::uint16_t;