Table of Contents
BE ENGINEERING INSIGHTS: Nicer Lines By Benoit Schillings - benoit@be.com
Even though standard line drawing looks good for horizontal, vertical, and 45 degree lines, it's sometimes useful to be able to draw at other angles without having the problem of "jaggies" on the lines. The solution to this problem has been known for at least 500 years -- it's called anti-aliasing. A common misconception is that anti-aliasing is expensive and will slow your critical application down to a crawl. So here's a little routine which shows you how to avoid the speed problem. It's actually only slightly slower than regular line drawing and still lets you have that nice anti-aliased look! The idea is to use a modification of the Bresenham algorithm, in which the error term is used to compute the relative brightness of two pixels. The parameters that compute the error term are pre-scaled, so they give an index directly into a color table. The current code is designed for an 8-bit bitmap, but it's a trivial exercise (which I leave to you) to convert it to 32-bit graphics. Note that in this simple implementation, there is not clipping code -- so be careful. Another simplification in the code is that the routine will not blend the color of the line with the existing background. In many cases, this approximation is OK, but you may need to read the old pixel and blend it with the new color before writing it back. If you do that, performance will suffer. The last parameter of the function (mp) is a pointer to a table of 32 char containing the palette to be used. An example of a palette could be a simple gray ramp: void main() { uchar palette[32]; BScreen s; rgb_color c; long entry; for (i = 0;i < 32; i++) { c.red = i * 8; c.green = i * 8; c.blue = i * 8; entry = s.IndexForColor(c); palette[i] = entry; } } And now the code... void anti_fline(BBitmap *b, long x1,long y1,long x2, long y2, uchar *mp) { long dx,dy; long sy; long rowbyte; long error; long cpt; uchar *base; float k; base = (char *)b->Bits(); //get bitmap base rowbyte = b->BytesPerRow(); //number of byte per row dx = x2-x1; // delta x dy = y2-y1; // delta y base = base + y1*rowbyte+x1; //compute start point address if (dx==0 && dy==0) { //single pixel case optimisation *base = *mp; return; } if (dy<0) { //if the line goes up sy = -1; //step up dy = -dy; //get dy positive again rowbyte = -rowbyte; //invert rowbyte to go up } else sy = 1; if (dx > 0) { //if going right if (dx > dy) { //select case, is it more horizontal than vertical? cpt = x2 - x1; //how many pixels do we draw k = (31*65536)/(dx); //init the error dy = (int)(dy*k); //scale the dithered variables dx = (int)(dx*k); error = dx >> 1; //init the error at half the delta x while(cpt>=0) { *base = mp[31 - (error>>16)]; //write a pixel cpt--; *(base+rowbyte) = mp[(error>>16)]; //and its complement one line up or down base++; //go right one pixel error += dy; //update the error dither if (error >= dx) { base += rowbyte; //go up or down one line if the error is greater than dx error -= dx; } } } else { //second case, more horizontal than vertical cpt = dy; //how many pixels to draw k = (31*65536)/(dy); dy = (int)(dy*k); dx = (int)(dx*k); //scale and init the dithered variables error = dy>>1; while(cpt >= 0) { *base = mp[31 - (error>>16)]; //write a pixel cpt--; *(base+1) = mp[(error>>16)]; //and its complement one to the right base += rowbyte; //go one line down (or up). error += dx; //update the error if (error >= dy) { base++; //go one right if necessary error -= dy; } } } } else { //basically the same code by going left dx = -dx; //instead of right if (dx > dy) { error = dx >> 1; //the code could be compacted by using cpt = dx; //a positive or negative value for the k = (31*65536)/(dx); //horizontal direction, but there is a dy = (int)(dy*k); //small performance advantage into writing dx = (int)(dx*k); //the explicit code. while(cpt>=0) { *base = mp[31 - (error>>16)]; cpt--; *(base+rowbyte) = mp[(error>>16)]; base--; error += dy; if (error >= dx) { base += rowbyte; error -= dx; } } } else { error = dy>>1; cpt = dy; k = (31*65536)/(dy); dy = (int)(dy*k); dx = (int)(dx*k); while(cpt >= 0) { *base = mp[31 - (error>>16)]; cpt--; *(base-1) = mp[(error>>16)]; base += rowbyte; error += dx; if (error >= dy) { base--; error -= dy; } } } } }
DEVELOPERS' WORKSHOP: Simplifying Media Nodes By Stephen Beaulieu - hippo@be.com
"Developers' Workshop" is a weekly feature that provides
answers to our developers' questions, or topic requests.
To submit a question, visit
[http://www.be.com/developers/suggestion_box.html].
Working with media and processing it in real time is
inherently complicated. That's why the new BeOS Media Kit is
complex, and requires greater attention to detail than other
BeOS programming. In preparation for next month's Be
Developer's Conference (BeDC), we've created a series of
convenience and utility classes to make programming Media
Kit nodes easier. In this article I'll give you a sneak preview of one of
these utility classes, and show you the direction we're
heading in for some of our convenience nodes. Note that
while the functionality of these classes will remain fairly
consistent, their interface is still a work in progress, and
may change in the final release. A fundamental concept of the Media Kit is that the media
being played or manipulated has the notion of time
associated with it. The job of a node is to handle this
media data at the right time, to make sure the end user sees
the correct information. All basic Media Kit control
messages also have associated times; a node is responsible
for properly managing all of these timed events. Enter the new Now, a quick look at the main functions of the class. Here, an event is represented by a performance time, a
'what' value, some pointer data, a flag denoting how the
pointer should be cleaned up, and an Events are usually pushed on to the queue in a node hook
function, for example: Here the pointer keeps track of the buffer to work with, the
cleanup specifies that the buffer should be recycled during
cleanup (if not sent correctly), and the data field is not
used. The When an event is popped off the queue it is usually handed
to a If you need to clear a large number of items out of the
queue without handling them, When flushing events you can specify a time from which to
base your decision, a direction in time, whether to include
the specified time in the flush, and the event type to clear
out. The function is very flexible, allowing you to remove
all items with a given event type ('what') like this: Or, you can clear out all types within a given range in time: In either case, when events are flushed from the queue, the
There are also two functions for investigating the contents of
the queue: The real advantage of this class is that it simplifies the
main loop of a node. A main loop with an event queue now
looks something like this in psuedo-code: If the queue does not have any events, or if it's not time for
the next event, the node loops over the When it's finally time to handle an event, the first event
is popped off the queue and This is a much easier to understand main loop, and as such
should be easier to get right. I've packaged up the You can find ChromeVideo at: ftp://ftp.be.com/pub/samples/media_kit/ChromeVideo.zip Don't miss the ReadMe, so you understand the contents of the
package. To see the class in action you need a supported video card
and the corresponding capture and display nodes. You can
find an alpha version of the Bt848 nodes and supporting
application for R4 at Steve Sakoman's site: These nodes are part of the upcoming 4.1 upgrade. This has been a first look at how we're trying to simplify the
writing of Media Nodes. If you want to know more, register for
the upcoming BeDC -- Programming with the Media Kit: http://www.be.com/developers/developer_conference/BeDC_info.html See you there!
Keeping our past newsletters on line has many advantages.
One of these is that they give readers a sense of the
history of our little company. Sometimes, sharp-eyed readers
kindly give us an opportunity to shed the light of logic, or
apology, on apparent contradictions. For instance, I wrote a
column titled "The War of Two Browsers" on
August 28th, 1996. In it, I wrote "In the meantime, we
happen to agree with Microsoft: the browser ought to be
integrated in the system and presented to application
programmers as an API, a class library. After all, several
years ago, we didn't call our 'user shell' a Finder or a
File Manager, we called it a browser." How does this square with our subsequent criticism of
Microsoft's integration of the Explorer Web browser in
Windows in subsequent columns: On February 3rd, 1999,
and on February 10th, 1999? Are we contradicting
ourselves? Did I even remember our August 1996 position when
I wrote my February 1999 columns? No and Yes. Let's start with two premises. First, the "border" issue,
and second, what you can no longer do once you've become a
monopoly. The "border" issue invoked here refers to the
difficulty of setting up a clean, well-lighted fence between
what we imprecisely call the operating system and the
applications. Today, it appears extremely difficult, some
say impossible, to write a usable English sentence as a test
of which is which. If the code meets condition A, it belongs
to the OS. We have trouble describing what "condition A"
would be. I don't know if the trouble lies more with the
speed of technological evolution making definitions
obsolete, or with the very protean nature of software. I
guess that it's more the latter than the former. In any event, how does one write laws or regulations
governing borders that are imprecise at any given moment --
and that move very fast once you unfreeze the clock. This
would seem to support Microsoft's right to put whatever they
please in Windows -- a spreadsheet, a word processor
(there's a decent one already, WinPad), or a Web browser
whose capabilities would help many other system modules or
applications -- if we stick to a common-sense but imprecise
definition for these two. Turning to the second issue: Things that change once you
become a monopoly. In a market where no player dominates,
common-sense and, it seems, the law both say that
exclusionary incentives can be offered. What I have in mind
is offering a retailer an incentive to distribute your brand
of yogurt and only your brand of yogurt. For instance, a
supplementary discount above the normal wholesale price.
Retailers who offer several brands of yogurt pay wholesale,
but the retailer who offers brand X exclusively gets a
supplementary rebate. In a truly competitive market, this is legit. Other
retailers exist who might bet customers will prefer a choice
of yogurt. Smaller margins on brand X might be compensated
by more volume, or more store traffic and the opportunity to
sell fidgets, bidgets and ridgets, not just an assortment of
yogurt. Once you become a monopolist, things change.
Retailers effectively offer only one brand, brand X. In
addition to the wholesale price, the retailer gets a
substantial supplementary discount based on not offering
and/or advertising any emerging new brand. If he does, he
loses a sizeable amount of money. Sales from the emerging
brand Y cannot make up for the loss, and his competitors who
didn't budge pay a lower price for the monopoly product. The
market is locked. This is but one example of what happens when you become a
monopoly: Behavior that's acceptable when the market is
truly competitive must be rejected once a monopoly situation
sets in. With this in mind, let's consider the manner and
the motives for Microsoft's integration of the Explorer
browser into Windows. They could have done it "fair and
square," one module (or a small number of modules) somewhere
in the system, a sort of Web driver (I realize the metaphor
is crude, but the point is valid nonetheless). Today, you
can update or replace a driver and, if the driver works as
intended or better, the user benefits, applications get
better performance, the system is improved. Even if a browser is more complicated that a dial-up module,
it features a finite list of pipes for information to come
in and go out of. One way of integrating the browser makes
it easy to disconnect and reconnect the pipes, another way
makes it deliberately difficult. Did Microsoft make the
disconnection and reconnection deliberately difficult and,
if they did, why? As a monopolist, are they free to make it
arbitrarily difficult to remove Explorer? Did they have an
anti-competitive motive for doing so? Did they leverage
their monopoly? Did they fear that another Web browser might
prevent them from gaining control of the HTML dialect mostly
spoken on the Web? In our case, we found we could offer a browser and make it
easily removable. In other words our browser is integrated
in the sense that it's "in the box" when you install the
BeOS, it's in the system. But if, in the future, you want to
install a competitive browser, it's a simple drag and drop
move. Integration doesn't have to be anti-competitive.
1997 Be Newsletters | 1995 & 1996 Be Newsletters Copyright ©1999 Be, Inc. Be is a registered trademark, and BeOS, BeBox, BeWare, GeekPort, the Be logo and the BeOS logo are trademarks of Be, Inc. All other trademarks mentioned are the property of their respective owners. |