Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

text: Take the gutter into account when masking text #18272

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 19 additions & 25 deletions core/src/display_object/edit_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,12 @@ impl<'gc> EditText<'gc> {
// This seems to be OS-independent
const INPUT_NEWLINE: char = '\r';

/// Gutter is the constant internal padding of a text field.
/// It applies to each side and cannot be changed.
///
/// See <https://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/text/TextLineMetrics.html>.
const GUTTER_WIDTH: Twips = Twips::new(40);

/// Creates a new `EditText` from an SWF `DefineEditText` tag.
pub fn from_swf_tag(
context: &mut UpdateContext<'gc>,
Expand Down Expand Up @@ -236,7 +242,7 @@ impl<'gc> EditText<'gc> {
&text_spans,
context,
swf_movie.clone(),
swf_tag.bounds().width() - Twips::from_pixels(Self::INTERNAL_PADDING * 2.0),
swf_tag.bounds().width() - Self::GUTTER_WIDTH * 2,
swf_tag.is_word_wrap(),
font_type,
);
Expand Down Expand Up @@ -726,16 +732,6 @@ impl<'gc> EditText<'gc> {
self.try_bind_text_field_variable(activation, true);
}

/// Construct a base text transform for this `EditText`, to be used for
/// evaluating fonts.
///
/// The `text_transform` constitutes the base transform that all text is
/// written into.

/// Internal padding between the bounds of the EditText and the text.
/// Applies to each side.
const INTERNAL_PADDING: f64 = 2.0;

/// Relayout the `EditText`.
///
/// This function operates exclusively with the text-span representation of
Expand All @@ -747,7 +743,7 @@ impl<'gc> EditText<'gc> {
let autosize = edit_text.autosize;
let is_word_wrap = edit_text.flags.contains(EditTextFlag::WORD_WRAP);
let movie = edit_text.static_data.swf.clone();
let padding = Twips::from_pixels(EditText::INTERNAL_PADDING) * 2;
let padding = Self::GUTTER_WIDTH * 2;

if edit_text.flags.contains(EditTextFlag::PASSWORD) {
// If the text is a password, hide the text
Expand Down Expand Up @@ -908,8 +904,7 @@ impl<'gc> EditText<'gc> {
// (instead of culling, this can be implemented as having the loop start from `scrollY`th line)
// (maybe we could cull-before-render all glyphs, thus removing the need for masking?)
// TODO: also cull text that's simply out of screen, just like we cull whole DOs in render_self().
if origin.y() + Twips::from_pixels(Self::INTERNAL_PADDING)
- edit_text.vertical_scroll_offset()
if origin.y() + Self::GUTTER_WIDTH - edit_text.vertical_scroll_offset()
> edit_text.bounds.y_max
{
return;
Expand Down Expand Up @@ -1283,8 +1278,8 @@ impl<'gc> EditText<'gc> {
pub fn screen_position_to_index(self, position: Point<Twips>) -> Option<usize> {
let text = self.0.read();
let mut position = self.global_to_local(position)?;
position.x += Twips::from_pixels(Self::INTERNAL_PADDING) + Twips::from_pixels(text.hscroll);
position.y += Twips::from_pixels(Self::INTERNAL_PADDING) + text.vertical_scroll_offset();
position.x += Self::GUTTER_WIDTH + Twips::from_pixels(text.hscroll);
position.y += Self::GUTTER_WIDTH + text.vertical_scroll_offset();

// TODO We can use binary search for both y and x here

Expand Down Expand Up @@ -1914,7 +1909,7 @@ impl<'gc> EditText<'gc> {
leading,
width: union_bounds.width(),
height: union_bounds.height() + descent + leading,
x: union_bounds.offset_x() + Twips::from_pixels(EditText::INTERNAL_PADDING),
x: union_bounds.offset_x() + Self::GUTTER_WIDTH,
})
}

Expand Down Expand Up @@ -1988,8 +1983,7 @@ impl<'gc> EditText<'gc> {
}

let bounds = edit_text.layout.char_bounds(index, text)?;
let padding = Twips::from_pixels(Self::INTERNAL_PADDING);
let bounds = Matrix::translate(padding, padding) * bounds;
let bounds = Matrix::translate(Self::GUTTER_WIDTH, Self::GUTTER_WIDTH) * bounds;
Some(bounds)
}

Expand Down Expand Up @@ -2049,8 +2043,8 @@ impl<'gc> EditText<'gc> {
let Some(mut position) = self.global_to_local(point) else {
return false;
};
position.x += Twips::from_pixels(Self::INTERNAL_PADDING) + Twips::from_pixels(text.hscroll);
position.y += Twips::from_pixels(Self::INTERNAL_PADDING) + text.vertical_scroll_offset();
position.x += Self::GUTTER_WIDTH + Twips::from_pixels(text.hscroll);
position.y += Self::GUTTER_WIDTH + text.vertical_scroll_offset();

text.layout.boxes_iter().any(|layout| {
layout.is_link()
Expand Down Expand Up @@ -2306,9 +2300,9 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {

context.commands.push_mask();
let mask = Matrix::create_box(
edit_text.bounds.width().to_pixels() as f32,
(edit_text.bounds.width() - Self::GUTTER_WIDTH * 2).to_pixels() as f32,
edit_text.bounds.height().to_pixels() as f32,
Twips::ZERO,
Self::GUTTER_WIDTH,
Twips::ZERO,
);
context.commands.draw_rect(
Expand All @@ -2322,8 +2316,8 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
// If this is actually right, offset the border in `redraw_border` instead of doing an extra push.
context.transform_stack.push(&Transform {
matrix: Matrix::translate(
Twips::from_pixels(Self::INTERNAL_PADDING) - Twips::from_pixels(edit_text.hscroll),
Twips::from_pixels(Self::INTERNAL_PADDING) - scroll_offset,
Self::GUTTER_WIDTH - Twips::from_pixels(edit_text.hscroll),
Self::GUTTER_WIDTH - scroll_offset,
),
..Default::default()
});
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/tests/swfs/text/auto_size/return/output.expected.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
76 changes: 76 additions & 0 deletions tests/tests/swfs/visual/edittext/edittext_gutter/Test.as
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package {
import flash.display.Sprite;
import flash.text.TextField;
import flash.text.TextFormat;

[SWF(width="100", height="110")]
public class Test extends Sprite {
[Embed(source="TestFont.ttf", fontName="TestFont", embedAsCFF="false", unicodeRange="U+0061-U+0062")]
private var testFont:Class;

private var nextX:int = 2;
private var nextY:int = 2;

public function Test() {
stage.scaleMode = "noScale";

newTextField("a", 0, 0);
newTextField("a", 1, 0);
newTextField("a", 2, 0);
newTextField("a", 3, 0);
newTextField("a", 4, 0);
newTextField("a", 5, 0);
newTextField("a", 6, 0);
newTextField("a", 7, 0);
newTextField("a", 8, 0);
newTextField("a", 40, 0);
nextY += 34;
nextX = 2;

newTextField("b", 0, 0);
newTextField("b", 1, 0);
newTextField("b", 2, 0);
newTextField("b", 3, 0);
newTextField("b", 4, 0);
newTextField("b", 5, 0);
newTextField("b", 6, 0);
newTextField("b", 7, 0);
newTextField("b", 8, 0);
newTextField("b", 40, 0);
nextY += 34;
nextX = 2;

newTextField("b", 6, 0);
newTextField("b", 6, 1);
newTextField("b", 6, 2);
newTextField("b", 6, 3);
newTextField("b", 6, 4);
newTextField("b", 6, 5);
newTextField("b", 6, 6);
newTextField("b", 6, 18);
newTextField("b", 6, 19);
newTextField("b", 6, 20);
newTextField("b", 6, 21);
newTextField("b", 6, 22);
}

private function newTextField(value: String, width: int, hscroll: int): void {
var text:TextField = new TextField();
text.border = true;
text.width = width;
text.x = nextX;
text.y = nextY;
text.height = 30;
text.embedFonts = true;
var tf = new TextFormat();
tf.font = "TestFont";
tf.size = 20;
text.defaultTextFormat = tf;
text.text = value;
text.scrollH = hscroll;
nextX += text.width + 2;
addChild(text);
}

}
}
83 changes: 83 additions & 0 deletions tests/tests/swfs/visual/edittext/edittext_gutter/TestFont.sfd
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
SplineFontDB: 3.2
FontName: TestFont
FullName: TestFont
FamilyName: TestFont
Weight: Regular
Copyright: Copyright (c) 2024, Kamil Jarosz
UComments: "2024-7-24: Created with FontForge (http://fontforge.org)"
Version: 001.000
ItalicAngle: 0
UnderlinePosition: -100
UnderlineWidth: 50
Ascent: 800
Descent: 200
InvalidEm: 0
LayerCount: 2
Layer: 0 0 "Back" 1
Layer: 1 0 "Fore" 0
XUID: [1021 253 198287149 6396829]
StyleMap: 0x0000
FSType: 0
OS2Version: 0
OS2_WeightWidthSlopeOnly: 0
OS2_UseTypoMetrics: 1
CreationTime: 1721856925
ModificationTime: 1728902792
OS2TypoAscent: 0
OS2TypoAOffset: 1
OS2TypoDescent: 0
OS2TypoDOffset: 1
OS2TypoLinegap: 90
OS2WinAscent: 0
OS2WinAOffset: 1
OS2WinDescent: 0
OS2WinDOffset: 1
HheadAscent: 0
HheadAOffset: 1
HheadDescent: 0
HheadDOffset: 1
OS2Vendor: 'PfEd'
MarkAttachClasses: 1
DEI: 91125
Encoding: ISO8859-1
UnicodeInterp: none
NameList: AGL For New Fonts
DisplaySize: -48
AntiAlias: 1
FitToEm: 0
WinInfo: 0 30 10
BeginPrivate: 0
EndPrivate
BeginChars: 256 2

StartChar: a
Encoding: 97 97 0
Width: 50
Flags: HW
LayerCount: 2
Fore
SplineSet
0 800 m 1
50 800 l 1
50 0 l 1
0 0 l 1
0 800 l 1
EndSplineSet
EndChar

StartChar: b
Encoding: 98 98 1
Width: 1000
Flags: HW
LayerCount: 2
Fore
SplineSet
0 800 m 1
1000 800 l 1
1000 0 l 1
0 0 l 1
0 800 l 1
EndSplineSet
EndChar
EndChars
EndSplineFont
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file.
Binary file not shown.
7 changes: 7 additions & 0 deletions tests/tests/swfs/visual/edittext/edittext_gutter/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
num_ticks = 1

[image_comparisons.output]
tolerance = 128

[player_options]
with_renderer = { optional = false, sample_count = 4 }
Loading