Formovie Theatre Projector Review

Until very recently, my two daughters shared a bedroom. My eldest has now reached the age that she wanted her own room - this is OK and something my wife and I had always anticipated. The condition for her getting her own room was that the "playroom" (a.k.a the plastic toy graveyard) would have to be sacrificed because the new bedroom was being used by her mum as a study. As luck would have it, my wife is changing jobs which means she doesn't really need a dedicated room for work anymore (just a desk to work at) and hence permission was given tome  turn the playroom into a "den" / occasional study.

I could have just mounted a TV on the wall and be done with it but I've long coveted a dedicated space for a projector. I already own a pretty good short throw 4K projector (the ViewSonic X10-4K) but that wasn't going to work in this room because it has to sit about 2 m in front of the screen to get a 90-100" image. Additionally, one of the conditions of the new den was that it had to function as an occasional study and look tidy most of the time and so permenantly setting up that projector in place was a no go.  After a lot of research I settled on the Formovie Theater.

The Formovie Theater is an ultra short throw (UST) projector, also known as a "laser TV". These projectors sit immediately underneath the screen (or wall) and project the image upwards. There's no mounting from the ceiling (with the associated technical challenges of getting power and HDMI into the roof) as with a traditional projector or no snaking of cables to a short throw projector that sits a metre or two in front of the screen like with my old ViewSonic projector. You literally plug the projector into a power socket and away you go.

First things first, this thing is pretty large and quite heavy. Not a big issue if you plan on plonking it down on top of a TV bench and forgetting about it but worth considering if you're thinking about getting it out every now. There are plenty of nice pictures of the unit on the web but here's a shot of it in place in the den next to a PS5 controller for scale:

Connectivity wise it's quite generous, particularly for an UST projector. It has three HDMI inputs (one of which is eARC), ethernet, optical out, line in and two USB-A ports.

It uses Android TV (version 11 as of the time of writing) as it's operating system. Honestly I haven't really explored it as I've got an AppleTV hooked up to it as my primary interface. I've heard good things about Android TV with the exception that you can't use the Netflix app (some sort of licensing issue). If you want to use Netflix with this you'll need a third party device (like an AppleTV, Fire stick, etc).

You can read the full specs on the Formovie website but this projector supports pretty much all the standards you'd like such as Dolby Vision, Dolby Atmos and 4K HDR. Apparently it does support ALLM (auto low latency mode) which automatically puts the projector into "game" mode when connected to a console but I haven't been able to get this to work yet. My PS5 says that my TV doesn't support it which is a little weird. I may just have to enable a setting somewhere.

Most projectors nowadays have some sort of built-in sound system and typically they are terrible. The Formovie Theater is an exception. Its audio is engineered by Bowers & Wilkins and it's phenomenal. I'm a bit of an audiophile and in my lounge I have the Sonos 5.1 system (Arc, Sub and two Play:1s) which I'm mostly very happy with. I had planned on migrating this sound system into the den but honestly I don't think I'm going to bother. Whilst I'm sure the Sonos is better than the projector's sound system I don't think it's worth the hassle of moving it from another room - I think that's pretty high praise.

Setting up the projector is straightforwards enough but is a little tedious. The size of the image is directly related to the distance from the back of the projector to the wall. To make the image larger you move the projector away from the wall, to make it smaller you move it closer. I've settled on a 92" screen and the back of the projector is 21 cm from the wall. Since the projector is 35 cm deep that means the front of the projector is 56 cm from the wall. Bear this in mind when considering the TV bench's position. Also, there's no real height adjustment, hence why you can see in the image above that the projector is sat on some wood. This is necessary to elevate the image to where I ended up mounting the screen. The projector does offer some keystone correction options which can fine tune a wonky image but that comes at the cost of (minor) image quality loss. I wanted to avoid this.

What's the picture like then? Well, when the projector first arrived I didn't have a projector screen so I just used the wall for the first few days. The wall is pale pink and pretty flat and not coated with anything special. The image was so good that I almost didn't buy a dedicated screen for it. My wife actually preferred it when there was no screen since the look was more minimalist. With a dedicated projector screen (I went for this 92" fixed frame one) I think the image is fantastic and is certainly brighter. I wrestled internally about whether to pay the extraordinary premium for an ALR (ambient light rejecting) screen but I don't think it's necessary and I'm glad I saved the £1000+ by just buying a "regular" 4K projector screen. This is a triple laser projector with a maximum brightness of 2800 ANSI lumens. From a practical perspective I can watch it with background illumination from the spotlights in the centre of the room's ceiling but I choose to watch it in the dark with the blinds pulled for that true cinema feeling. The image is super bright, absolutely crisp and gorgeous. In my subjective opinion it's better than my one year old OLED LG 55" TV and blows my ViewSonic out of the water. I'm very happy with it.

For the curious, here's the current setup:

Storing Text With A Gap Buffer

Writing a code editor control is more difficult than you might imagine for many reasons. Not only do editors need to look nice and be extensible but importantly they need to be fast. Nobody is going to enjoy writing / coding if there's a noticeable delay when typing or manipulating text. One of the fundamental components of a code editor that impacts performance is how it stores text internally.

There are many ways an editor control could choose to store text. The most naive approach would be to just keep the contents as a String, appending to it as you type and using Left(), Right() and Middle() to extract sub parts needed (e.g. when removing or replacing contiguous characters). Trust me when I say that this doesn't work well once you get more than a few hundred characters.

Another approach to consider is that used by XUICodeEditor. XUICodeEditor uses an array of Line classes that store only the characters on that line in the form of a String array. Conceptually this is pretty easy to understand and feels like a sensible swing at the problem. This solution is reasonably fast since there are rarely more than a hundred or so characters on a line. A significant drawback is that it's slow to get all the text in the editor as we have to iterate each line and concatenate. This makes it challenging to parse a document without a lexer that can manage only being able to see the current line (and perhaps the line before and afterwards).

A third, better, but not not ideal choice would be to store the entire document as an array of characters. This makes adding and removing characters at specific indices quick and easy. It's also straightforward enough to append characters. There are challenges involved in extracting arbitrary runs of characters from within the array since you have no option with Xojo other than to loop over the array and extract the characters. A major downside with this approach is that appending single characters either within the middle of the array or even at the the end with Array.Add requires Xojo to dynamically resize the array for each character added. Once the document has a few hundred characters, you'll start to notice the time it takes for this resizing to occur.

OK smart arse, what's the solution then? Well there are many but in my opinion the best approach is to use a gap buffer.

Gap buffer to the rescue

A gap buffer is a data structure that leverages the fact that changes to the contents of a document tend to occur around an area of interest. This makes sense if we consider the caret as the area of interest. Editing a document usually involves bursts of typing to insert characters immediately after the caret.

The principle of a gap buffer is that, within the data structure, you have the text stored as two contiguous areas of text separated by a gap. It's within this gap that new text can be inserted efficiently.

Consider a normal array where each element is a character:

|0|1|2|3|4|5|6|7|
|h|e|l|l|o|y|o|u|

To add a hyphen between "hello" and "you" we need to grow the array since there are only 8 elements and we need 9:

|0|1|2|3|4|5|6|7|8|
|h|e|l|l|o|-|y|o|u|

In Xojo, the framework would create a new 9 element array and copy all the characters of the old array to their new positions. You don't see this as it's all handled for you by Array.InsertAt but that is what's happening behind the curtain.

A gap buffer on the other hand would look something like this:

|0|1|2|3|4|5|6|7|8|9|a|b|c|
|h|e|l|l|o| | | | | |y|o|u|
          ^

Where ^ is the start of the gap.

The buffer's underlying data storage (which could be an array or a MemoryBlock) has two contiguous areas of text ("hello" and "you") but there is a gap of unused elements between them. As in the earlier case, to insert a hyphen between "hello" and "you" we do this:

|0|1|2|3|4|5|6|7|8|9|a|b|c|
|h|e|l|l|o|-| | | | |y|o|u|
            ^

Notice how we haven't needed to grow our backing store at all. This means we haven't had to copy / move anything. All that was required was a write to an index and adjusting the gapStart property. So long as the user keeps typing within the gap the operation will be fast.

Once the gap gets filled up then we need to create a new backing store and copy the data over. We'll ensure that the new data store has another gap and the process repeats. To edit characters anywhere in the document we just need to move the gap. Moving the gap does have a performance penalty as we need to grow the backing store but over the course of all operations, that cost is amortised. The end result is a text storage solution that is fast enough for a text editor.

Implementation

Obviously I didn't invent the idea of a gap buffer. My code is inspired by code from Alex Restrepo's CustomEditField as well as several other open source implementations in other programming languages.

You can find the implementation in the SyntaxArea GitHub repository. As of this post, the latest commit was 1a9903e.

We need two classes and a class interface for our implementation. Strictly speaking you could ditch the class interface (ITextStorage) but since this class will be used in my SyntaxArea project, I'm going to future proof things in case I find an even more efficient way to store text (such as a rope) in the future. By incorporating an interface I could in theory swap the gap buffer out for something better down the road.

ITextStorage

First up, the interface. These are the methods that our GapBuffer class will implement. As you can see, we have methods for inserting, removing, replacing and retrieving text at specified indexes over arbitrary lengths.

GapBufferData

Our GapBuffer class internally will use a MemoryBlock to store the actual characters of text. I've wrapped that up in a class that provides some useful methods. In theory, we could update the GapBuffer class to use a different data store if we figure out a better one.

The class has just one property, mStorage which is a MemoryBlock in which we will store each character in UTF-32 format. Why UTF-32? Well in this encoding each character is fixed at 4 bytes. An encoding like UTF-8 uses a variable number of bytes for each character (between 1 and 4). Whilst UTF-32 will use more memory, it makes indexing into the MemoryBlock to retrieve and set a character much easier. It sacrifices more memory but on a modern computer that is not an issue.

The constructor is basic - we simply pass it the size (number of characters to store) and we multiply that by a class constant BYTES_PER_CHAR (4) to get the correct number of bytes the MemoryBlock needs to be.

The class has a computed property, Size which retrieves the maximum number of characters that the class can contain. It is not the number of characters in the store. In the code you'll note how we need to multiply or divide by the constant BYTES_PER_CHAR since we use 4 bytes for every character stored.

There's a Copy() method that copies a portion (or all) of one GapBufferData instance into another. Lastly we have two overloaded methods, StringValue(). These either retrieve or set a run of characters.

GapBuffer

The meat of the action resides in the GapBuffer class. This implements the ITextStorage interface. The code comprehensively documented so should be easy to understand. There's not a lot in the methods - that's part of the beauty of gap buffers (and the secret to their great performance) - they are fairly simple data structures.

New Project: SyntaxArea

I feel like I'm constantly reinventing the wheel.

I have an app in mind that I want to build with Xojo. A central component of that app is a text editor that's able to colourise Markdown, HTML and a few programming languages. There are several code editors available to the Xojo developer (including a pretty good one that I wrote and currently sell called XUICodeEditor) but none of them meet all of my requirements so I'm going to create my own (again).

The editor must meet the following criteria:

  1. Fast enough to edit documents containing hundreds of lines of code / Markdown without slowing down.
  2. Be able to highlight different languages within the same document. For example, Xojo code within a code fence of a Markdown document.
  3. Fully API 2.0.
  4. Support autocompletion.
  5. The syntax highlighting colours and the colours of all the components of the editor must be user customisable.
  6. Fully supports dark mode.
  7. Handles emoji.

I looked at the following code editors available for Xojo:

XUICodeEditor

My commercial code editor for Xojo. Don't get me wrong, I think this is a great editor for most developers. The only requirement from above that it doesn't meet is that it's not fast enough to handle documents containing hundreds of lines of code. In some respects this is by design. The original use case for XUICodeEditor was to highlight small blocks of code, perhaps a hundred lines or so. For that purpose it works very well. The other issue with XUICodeEditor is that it highlights characters using small parser classes which, by their nature, are inherently slower than regex-based highlighters. Also, they're a bit more involved to write.

CustomEditField

CustomEditField is a venerable syntax highlighting text area based on the native Xojo canvas. It was originally written many years ago by Alex Restrepo and is now maintained on GitHub by Thomas Tempelmann. This is an excellent editor and I thought it would work in this new project but it's API 1.0 so is a no-go. That's a shame because it handles enormous documents with no slow down.

MonkeyBread Scintilla Plugin

Hardly surprising but Christian over at MonkeyBread Software has a source code editor plugin. I say hardly surprising because Christian seems to have a plugin for everything! His plugin wraps the Scintilla editor and makes it available to Xojo developers. I don't mind using a closed source plugin if necessary but the issue with Scintilla is adding support for new languages ("lexers") has to be done via C and Christian would need to add them. Additionally, the existing lexer for Markdown does not highlight code within code fences so this won't work either.

GraffitiSyntaxEditor

The last contender on my list is Anthony Cypher's GraffitiSyntaxEditor. This isn't a bad option - Anthony has essentially wrapped the Javascript-based open source Ace editor editor. I have a couple of concerns however. Firstly it's ridiculously expensive. I really cannot justify $399 dollars per year to use it. Secondly, whilst Anthony is very responsive, I would likely be beholden to him to add new lexers to the editor.

What I've decided to do is create a brand new text editor. It's called SyntaxArea and I'm planning on developing it in the open with the code freely visible to anyone. It already has it's own GitHub repository. You might wonder why on Earth would I create a competitor to XUICodeEditor and give it away for free. Well, I think developing this editor will be interesting and will give me something to write about here and if the code is open source then that makes it easier to do so. Furthermore, I'm going to use some of the techniques in CustomEditField in SyntaxArea and since that's open source I feel better about using Alex and Thomas' code if I also make SyntaxArea open source.

I've already begun working on the project. I'll warn you about the repository however. The code will almost certainly frequently break in the early phases as I iterate on how best to implement something. I'm pretty confident this will ship since I've already written a full featured editor previously so I know I can do it.

Subscribe to garrypettet.com

Sign up now and never miss a new post. It's free.
Jamie Larson
Subscribe