Bitmap Surfaces in Trident
Date Last Updated:
Author: Michael Winser
Status: Draft
Version: 0.3

Table of Contents

Overview

Common interface for all bitmap surfaces

Bitmap surfaces are used in Trident in a variety of forms: DIBs, DIB sections, HBITMAPs and DCs. Although GDI handles each of these in a slightly different manner, they are all essentially the same thing: an array of bits called a bitmap. The goal is to define a common set of interfaces that encapsulates all existing uses of bitmaps in Trident as well as providing for future use of bitmaps from DirectDraw.

A bitmap will be represented by an IBitmapSurface object. This object may also be QI'd for IGdiSurface and IDDSurface. These interfaces provide a way to get at the the GDI and DirectDraw objects associated with a particular surface. Bitmaps surface are generally created by bitmap surface factories. The main interface for this is IBitmapSurfaceFactory. This object may also be QI'd for IGdiSurfaceFactory and IDDSurfaceFactory.

Direct access to bitmap bits will result in new features and performance improvements. For example, while transparent blts can be accomplished via GDI, they typically require three passes and an intermediate mask. With direct access to the surface bits, the blt can be done in a single pass with no mask. Alpha channel bitmaps are an example of functionality not currently supported by GDI that is almost trivial given direct access to the surface bits.

The IBitmapSurface interface has uses beyond its immediate use in Trident. This is particularly apparent with the use of an IBounds object instead of a simple RECT for coordinate information. By using an interface pointer instead of a structure it becomes possible to QI the object for extra bounds information such as time. This is particularly important when this information must "pass through" a component that doesn't know about or check for the new bounds information.

Centralized allocation and creation of off-screen surfaces

Trident currently has two off-screen DC classes and allocates DIBs in several places. By centralizing all surface allocation, it will be possible to more efficiently allocate and free surfaces, especially ephemeral surfaces used only during paint.

A central surface factory will enable a per-process or per-thread cache of discardable surfaces. Not only will this cache be used by internal Trident components, it will be available to external controls. This means that smart controls can participate in the application level cache intead of competing with the main application for working set.

There are several factory interfaces that will be provided via IServiceProvider. IBitmapSurfaceFactory is the preferred way to create arbitrary surfaces, IGdiSurfaceFactory is used to create surfaces compatible with an HDC.

While the initial implementation of the allocator will probably just use CreateDIBSection, a central allocator makes it very easy to add support for DirectDraw later on.

Allow controls to get at surface bits

The primary drawing interface for controls is still IViewObject::Draw. In order to allow controls to get at the destination surface bits, the IGdiSurfaceFactory interface has a GetSurfaceFromDC function. This solution was prefered over adding yet another control interface for which the container would have to check. The control would typically get the IGdiSurfaceFactory interface pointer once (at creation) and then map the DC to a surface when asked to paint.

Support DirectX hardware acceleration

DirectX provides driver level access to the graphics hardware. DirectDraw provides frame buffer and bltter access and Direct3D provides 3d rendering. Some of the benefits of DirectDraw include hardware transparent blt, page flipping, vertical retrace timing, tear free blts. While DirectDraw uses COM, it is unfortunately not aggregateable. Furthermore, several DirectDraw operations will grab the Win16 mutex. The IBitmapSurface implemention in Trident will therefore delegate to DirectDraw as necessary.

However, controls can and should be able to use DirectDraw when it is available. The IDDSurface interface (QI'd from an IBitmapSurface object) allows the caller to get the underlying IDirectDraw and IDirectDrawSurface objects. Not only does this allow controls direct access, it also allows them to coordinate this with other DirectX support such as Direct3D.

Interfaces

IBounds

An IBounds interface is used to describe and compare the size of an object. The initial IBounds interface describes pixel bounds only but the concept can be extended to support other dimensions (e.g. time, frames, metric size). These other dimensions are obtained by QI'ing the IBounds object for new interface (e.g. ITimeBounds, IFrameBounds, IMetricBounds).

This extensibility is why an interface is used instead of a simple structure. Components can be written to know about only the dimensions they need to accomplish a task and still work with more complex objects. For example, a transparent blt function might be declared as

HRESULT TransparentBlt(IBitmapSurface *pSurfaceSrc, IBounds *pBoundsSrc, 
                       IBitmapSurface *pSurfaceDst, IBounds *pBoundsDst);

The function obviously needs to know about the pixel coordinates of the operation but has no interest or knowledge of time. It simply calls LockBits on the surface parameters and does it's work. And yet, if the surface parameters are multi-framed surfaces, they could QI for IFrameBounds or ITimeBounds on the bounds parameter in order to determine which sub-surface to actually lock.

Some thought was given to making IBounds a dimensionless interface and then requiring components to QI for something like IPixelBounds that would return rectangles in pixel coordinates. While we may define a base interface IGenericBounds in the future, there is little point in doing so for a set of interfaces where pixels are the common denominator. The extra QI wouldn't be very expensive but can easily be avoided.

interface IBounds : IUnknown
{
	HRESULT Clone(IBounds **ppBounds);
	HRESULT GetRect(int *pLeft, int *pTop, int *pRight, int *pBottom);

	HRESULT SetRect(int left, int top, int right, int bottom);

	HRESULT IBounds::CopyFromBounds(IBounds *pBounds);

	HRESULT IsEqual(IBounds *pBounds);
	HRESULT IntersectBounds(IBounds *pBoundsSrc, IBounds **ppBoundsResult);
	HRESULT UnionBounds(IBounds *pBoundsSrc, IBounds **ppBoundsResult);
}

HRESULT IBounds::Clone(IBounds **ppBounds)

Parameters: Return values: Description:

Makes a copy of the bounds object. By using this function instead of explicitly copying information, the caller ensures that all other information (time, metric size, etc.) is also copied.

HRESULT IBounds::CopyFromBounds(IBounds *pBounds)

Parameters: Return values:

Description:

The bounds objects takes on the values of all common dimensions from the source object in pBounds. This is not a copy. This does not add new dimensions to the object.

The benefit of this function is that the bounds object may know of dimensions that the caller does not. For instance, two bounds objects may both contain time information. Even if the caller is unaware of this dimension, this function ensures that all common dimensions are copied. While this is better than having the caller simply copy the rectangle information, callers should be aware that source dimensions not present in the destination will not be copied.

HRESULT IsEqual(IBounds *pBounds, BOOL fMustMatchAllDimensions)

Parameters:

Return values:

Description:

Compares the bounds object to the parameter pBounds. The object will compare itself to pBounds on all its dimensions. If fMustMatchAllDimensions is TRUE then pBounds the the object will QI pBounds for all of it's dimensions. If pBounds does not support a dimension, the function will return S_FALSE. When fMustMatchAllDimensions is FALSE then only the common dimensions are compared.

Should we call this CompareBounds to make it a bit more clear that this isn't a true test of equality. Perhaps we should have CompareBounds as described above and IsEqual (possibly as a helper function) that does the compare in both directions.

pBounds1->CompareBounds(pBounds2) == S_OK  && pBounds2->CompareBounds(pBounds1) == S_OK)

HRESULT GetRect(int *pLeft, int *pTop, int *pRight, int *pBottom)

Parameters: Return values: Description:

Gets the rectangular bounds of the object.

HRESULT SetRect(int left, int top, int right, int bottom)

Parameters: Return values: Description:

Sets the rectangular bounds of the object.

HRESULT IntersectBounds(IBounds *pBoundsSrc, IBounds **ppBoundsResult);

Parameters: Return values: Description:

The source bounds object is checked for intersection on all the dimensions of the bounds object. The result is returned in ppBoundsResult. The semantics of this function are identical to those of the IntersectRect call in the Win32 API.

HRESULT UnionBounds(IBounds *pBoundsSrc, IBounds **ppBoundsResult);

Parameters: Return values: Description:

The source bounds object unioned on all the dimensions of the bounds object. The result is returned in ppBoundsResult. The semantics of this function are identical to those of the IntersectRect call in the Win32 API.

IBitmapSurface

The IBitmapSurface encapsulate a bitmap. Through this interface, callers can get at the underlying image bits. Bitmaps come in all widths, heights, and most importantly formats. Instead of describing the various attributes of a bitmap format in a structure, each bitmap format is uniquely identified by a GUID.

typedef GUID BFID;

The BFID embodies, bit depth, color space, how the bits are arranged in memory. Essentially everything about a format except the width, height and pitch of the bitmap. This will allow components to recognize formats with impunity, it isn't possible to forget to check some aspect of the description structure. Because BFIDs are GUIDs, ISVs can define their own formats with no danger of collision with other formats. The following formats will be defined:

BITMAPINFOHEADER equivalents
Format name biPlanes biBitCount biCompression Pixel format
BFID_MONOCHROME 1 1 BI_RGB same as DIB
BFID_GRAYSCALE 1 8

BI_RG

B

same as DIB
BFID_RGB_1 1 1 BI_RGB same as DIB
BFID_RGB_4 1 4 BI_RGB same as DIB
BFID_RGB_8 1 8 BI_RGB same as DIB
BFID_RGB_555 1 16 BI_RGB same as DIB
BFID_RGB_565
1 16 BI_RGB same as DIB
BFID_RGB_665 1 16 BI_RGB same as DIB
BFID_RGB_24 1 24 BI_RGB same as DIB
BFID_RGB_32 1 32 BI_GB same as DIB
BFID_RGB_A_32 1 32

For most of the above formats, the layout and meaning of the bits corresponds to a standard format in Windows DIBs. Nonetheless, each format will be fully described in a separate document. Not all of these formats will be implemented in Trident.

interface IBitmapSurface : IUnknown
{
	HRESULT Clone(IBitmapSurface **ppBitmapSurface);

	HRESULT GetFormat(BFID *pBFID);
	HRESULT GetFlags(DWORD *pdwFlags);

	HRESULT GetFactory(IBitmapSurfaceFactory **ppBitmapSurfaceFactory);
	HRESULT GetBounds(IBounds **ppBounds);
	HRESULT GetPitch(int *pPitch);

	HRESULT LockBits(IBounds *pBounds, DWORD dwLockFlags, void **ppBits);
	HRESULT UnlockBits(IBounds *pBounds, void *pBits);
	HRESULT Clone(IBitmapSurface **ppBitmapSurface);
}
Parameters: Return values: Description:

Creates a clone of the bitmap surface. This includes any extra information that may be available through other interfaces (e.g. color profile).

HRESULT GetFormat(BFID *pBFID);

Parameters: Return values: Description:

Returns the format ID of the bitmap surface.

HRESULT GetFactory(IBitmapSurfaceFactory **ppBitmapSurfaceFactory);

Parameters: Return values: Description:

Returns a pointer to the surface factory.

HRESULT GetFlags(DWORD *pdwFlags)

Parameters:

Return values:

Description:

Returns flags that describe some attributes of the surface. These flags are the same set passed into CreateBitmapSurface but not necessarily the same value.

Is this really needed? We need to identify scenarios in which this is actually used. I can't help but feel that this could be eliminated if the semantics of the flags was more precisely defined in CreateBitmapSurface. Are these flags hints or requirements? If I don't specify discardable, does that mean that the surface will never be discardable?

The only flag that I can't infer from QI'ing for another interface is SURFACE_DISCARDABLE. I've been thinking that we may want some kind of cache control interface which may be just the ticket.

In all likelyhood these questions will be resolved during development. Some flux should be expected.

HRESULT GetBounds(IBounds **ppBounds);

Parameters: Return values: Description:

Returns a pointer to the bounds object for this surface. This is a tearoff interface. The returned IBounds object has a lifetime that is independent of the bitmap surface. The top left corner of the rectangle of this bounds object is always left == 0 and top == 0.

HRESULT GetPitch(int *pPitch);

Parameters: Return values: Description:

The pitch is the amount to add to the address of pixel (x,y) to address pixel (x, y + 1)

HRESULT LockBits(IBounds *pBounds, DWORD dwLockFlags, void **ppBits);

Parameters:

Return values:

Description:

LockBits is used to get a pointer to the actual bitmap bits. It is assumed that the caller has checked the BFID and can actually interpret the format. All calls to LockBits must be matched with an equivalent call to UnlockBits. If pBounds is NULL then the entire surface is locked.

Although LockBits must be matched with UnlockBits, these calls do not AddRef the object. The lifetime of the object is independent of the lock count. This means that if an object is released while locked, the caller may still have a pointer to the bits. That pointer is invalid and must not be used.

The flags in dwLockFlags are independent bits and are defined as follows:

SURFACE_LOCK_EXCLUSIVE means that the caller wants exclusive access to the bounds specified. There can only be one exclusive lock on a given part of the bitmap. The granularity with which this is enforced is implementation dependent. For some implementations, it will apply to the entire object. Some implementations will track bounds rects more precisely and allow multiple non overlapping exclusive locks.

SURFACE_LOCK_ALLOW_DISCARD means that the caller doesn't care if the surface was discarded, the implementation can provide new bits and should return S_SURFACE_DISCARDED. If this flag is not specified and the surface was discarded, no surface pointer will be returned and E_SURFACE_DISCARDED will be returned.

HRESULT UnlockBits(IBounds *pBounds, void *pBits);

Parameters:

Return values:

Description:

UnlockBits unlocks the bits. The pointer in pBits must be considered invalid after this call. The pBounds, pBits pair must be exactly the same pointers that were passed to the matching LockBits call. This allows the implementation to use the pBounds, pBits pair as a unique cookie identifying a particular lock.

As with LockBits, pBounds == NULL is the entire surface.

UnlockBits may also be called without a matching LockBits, if and only if pBits is NULL. This is a hint to the implementation that the bits may be discarded (see IBitmapSurfaceFactory::CreateSurface).

IGdiSurface

interface IGdiSurface
{
	HRESULT GetDC(HDC *pHDC, DWORD dwLockFlags);
	HRESULT ReleaseDC(HDC hdc);
}

HRESULT GetDC(HDC *pHDC, DWORD dwLockFlags);

Parameters: Return values: Description:

Gets or creates an HDC onto the bitmap surface. The lock flags have the same semantics as the IBitmapSurface::LockBits call. All calls to GetDC must be matched by a call to IGdiSurface::ReleaseDC.

HRESULT ReleaseDC(HDC hdc);

Parameters: Return values: Description:

IDDSurface

interface IDDSurface
{
	HRESULT GetDirectDraw(IDirectDraw **ppDirectDraw);
	HRESULT GetDirectDrawSurface(IDirectDrawSurface **ppDirectDrawSurface);
	HRESULT ReleaseDirectDrawSurface(IDirectDrawSurface **ppDirectDrawSurface);
}

HRESULT GetDirectDraw(IDirectDraw **ppDirectDraw);

Parameters: Return values: Description:

Returns the Direct Draw object used to create the surface. The interface pointer returned has a COM identity and lifetime of it's own.

HRESULT GetDirectDrawSurface(IDirectDrawSurface **ppDirectDrawSurface);

Parameters: Return values: Description:

Returns the Direct Draw surface used to create the surface. The interface pointer returned has a COM identity and lifetime of it's own. However, the caller will be sharing the IDirectDrawSurface interface with the IDDSurface object. For this reason, the caller should not use AddRef and Release on the obtained object. Instead, ReleaseDirectDrawSurface should be called.

Note, we may remove this requirement should it proove to be totally unecessary but we're leaving it in for now.

HRESULT ReleaseDirectDrawSurface(IDirectDrawSurface *pDirectDrawSurface);

Parameters: Return values: Description:

Releases the IDirectDrawSurface object. Callers must use this instead of simply Releasing the object.

IBitmapSurfaceFactory

interface IBitmapSurfaceFactory
{
	HRESULT CreateBitmapSurface(int width, int height, BFID *pBFID, DWORD dwHintFlags, IBitmapSurface **ppBitmapSurface);
	HRESULT GetSupportedFormatsCount(unsigned *pcFormats);
	HRESULT GetSupportedFormats(unsigned cFormats, BFID *pBFIDs);
}

HRESULT CreateBitmapSurface(int width, int height, BFID *pBFID, DWORD dwFlags, IBitmapSurface **ppBitmapSurface);

Parameters: Return values: Description:

Creates a bitmap surface. The dwFlags parameter is a hint to the implementation as to the desired use of the surface. The possible values for dwFlags are:

If SURFACE_ATTRIBUTES_REQUIRED is specified and the implementation cannot fulfill the requested hints (notably SURFACE_GRAPHICS and SURFACE_HARDWARE), E_SURFACE_UNKNOWN_FORMAT will be returned. This is to allow components that require hardware access to either get it or fail early on.

HRESULT GetNumberSupportedFormats(unsigned *pcFormats);

Parameters:

Return values:

Description:

Returns the number of bitmap formats supported by this factory. While this number cannot change for a given IBitmapSurfaceFactory instance, it can change from instance to instance of the IBitmapSurfaceFactories, even from the same implementation.

HRESULT GetSupportedFormats(unsigned cFormats, BFID *pBFIDs);

Parameters:

Return values:

Description:

Returns an array of BFIDs that the factory supports

IGdiBitmapSurfaceFactory

interface IGdiBitmapSurfaceFactory
{
	HRESULT CreateCompatibleBitmapSurface(HDC hdc, DWORD dwHintFlags, IBitmapSurface **ppBitmapSurface)
	HRESULT GetFormatFromDC(HDC hdc, BFID *pBFID);
	HRESULT GetSurfaceFromDC(HDC hdc, IBitmapSurface **ppBitmapSurface);
}

HRESULT CreateCompatibleBitmapSurface(HDC hdc, int width, int height, DWORD dwHintFlags, IBitmapSurface **ppBitmapSurface)

Parameters: Return values: Description:

This is more or less equivalent to CreateCompatibleDC and CreateDIBSection. Like IBitmapSurfaceFactory::CreateBitmapSurface, the width and height must be > 0.

HRESULT GetFormatFromDC(HDC hdc, BFID *pBFID)

Parameters: Return values: Description:

Returns the format of a compatible surface for this HDC. If the HDC does not have an surface format (e.g. a postscript printer), E_SURFACE_NOSURFACE is returned.

HRESULT GetSurfaceFromDC(HDC hdc, IBitmapSurface **ppBitmapSurface)

Parameters: Return values: Description:

This function is used to find the surface associated with an HDC. When the DC is a screen DC, the surface can only be returned if DirectDraw is available. The surface object returned may change from call to call. This is because the surface object may be dynamically created (especially with DirectDraw) at the time of the call.

IDDSurfaceFactory

interface IDDSurfaceFactory
{
	HRESULT CreateBitmapSurface(IDirectDrawSurface *pDDSurface, IBitmapSurface **ppBitmapSurface)
	HRESULT GetFormatFromDDSurface(IDirectDrawSurface *pDDSurface, BFID *pBFID);
	HRESULT GetSurfaceFromDD(IDirectDrawSurface *pDDSurface, IBitmapSurface **ppBitmapSurface)
}

HRESULT CreateBitmapSurface(IDirectDrawSurface *pDDSurface, IBitmapSurface **ppBitmapSurface)

Parameters: Return values: Description:

Creates a BitmapSurface from a DirectDraw surface. This interface is used by components that have acquired a DirectDraw surface through other means and wish to wrap it as an IBitmapSurface. The IDirectDrawSurface object will be AddRef'd, to be released only when the created IBitmapSurface object is itself destroyed. This function will return E_SURFACE_UNKNOWN_FORMAT if the format IDirectDrawSurface is not supported by the surface factory.

HRESULT GetFormatFromDDSurface(IDirectDrawSurface *pDDSurface, BFID *pBFID);

Parameters: Return values: Description:

Returns the format of the Direct Draw surface

HRESULT GetSurfaceFromDD(IDirectDrawSurface *pDDSurface, IBitmapSurface **ppBitmapSurface)

Parameters: Return values: Description:

Returns the IBitmapSurface object associated with a Direct Draw surface. Much like the GetSurfaceFromDC call of IGdiSurface, this function attempts to find the associated IBitmapSurface object for a given IDirectDrawSurface. If the IDirectDrawSurface object was not created by the IDDSurfaceFactory implementation, E_SURFACE_NOSURFACE will be returned.

IRGBColorTable

A color table is used when the bitmap format is indexed. In this case, the pixel data does not contain the actual RGB values of the pixel. Instead it contains an index into an color table. When supported, this interface is available via QI from an IBitmapSurface. The following formats should always implement IRGBColorTable: BFID_RGB_1, BFID_RGB_4, BFID_RGB_8.

The transparent color index is used to indicate what is also known as a chromakey. Although the exact interpretation of this color depends on the appliation, this color should be skipped over during blts and ignored when dithering.

A special color index COLOR_NO_TRANSPARENT (0xffffffff) is used to indicate no colors indices are transparent.

interface IRGBColorTable
{
	HRESULT GetCount(unsigned *pCount);
	HRESULT SetCount(unsigned count);
	HRESULT GetColors(unsigned iFirst, unsigned count, RGBQUAD *pColors);
	HRESULT SetColors(unsigned iFirst, unsigned count, RGBQUAD *pColors);
	HRESULT GetTransparentIndex(unsigned *pIndex);
	HRESULT SetTransparentIndex(unsigned index);
}

HRESULT GetCount(unsigned *pCount);

Parameters: Return values: Description:

Returns the number of colors in the color table.

HRESULT SetCount(unsigned count);

Parameters:

Return values:

Description:

This function changes the size of the color table. Callers should not that this call does not change the image bits in any way. Notably, it is quite possible to set the count such that pixels in the bitmap refer to invalid colors. The interpretation of those pixels is implementation dependent and could cause some implementations to crash.

HRESULT GetColors(unsigned iFirst, unsigned count, RGBQUAD *pColors);

Parameters: Return values: Description:

Copies the RGBQUAD of the color table into the array. If iFirst + count exceeds the size of the color table, no colors are copied and E_FAIL is returned. This is specifically to prevent callers from assuming that color tables are the usual size (256).

HRESULT SetColors(unsigned iFirst, unsigned count, RGBQUAD *pColors);

Parameters: Return values: Description:

Sets the color table from an array of RGBQUADs. This will never change the underlying object's bits. If iFirst + count exceeds the size of the color table, E_FAIL is returned an no colors are copied.

HRESULT GetTransparentIndex(unsigned *pIndex);

Parameters: Return values:

Description:

Returns the index of the transparent color. If there are no transparent color indices, *pIndex will contain the special value: COLOR_NO_TRANSPARENT.

HRESULT SetTransparentIndex(unsigned index);

Parameters: Return values: Description:

Sets the index of the transparent color. Use COLOR_NO_TRANSPARENT to indicate that there is no transparent index.

Open Issues

Automation compatiblity.

Most of the above interfaces are mostly automation compatible. While it is not expected that high level automation clients (like VB) will actually manipulate bitmap surface bits, it is quite reasonable to assume they will be used to coordinate between multiple lower level components that do in fact access the bits.

DirectDraw controls

Some controls require DirectDraw or other DirectX capabilities in order to work. Unfortunately, DirectX functionality is parcelled up into tiny caps flags that make it almost impossible for the container to determine if the control can be used or not. Nonetheless, we hope to define (or see defined) some broad component categories that define common areas of DirectX. This will help the container determine how to create surfaces for a given control.

Pitch or Stride?

Should we rename GetPitch to GetStride for naming compatibility with DirectDraw? Obviously this is a cosmetic and documentation issue only.

Cache control

This document has only touched on the possibility of caching and discardable surfaces. The actual implementation and behaviour of the cache will be described in a separate document. That document must also describe how caching decisions are made and controlled.

Color tables and transparency

We need to define the exact semantics of the various IRGBColorTable members. Is the transparent color index considered to be one of the color table? Can the color table actually be resized or are we simply changing the number of colors used?

Not Addressed

Palettes

Although palettes and color tables are closely related, this document does not address any palette issues. This includes output to a palettized device or palette animation.

IDIBSurfaceFactory

Although not needed for Trident, it is not unreasonable to imagine an interface called IDIBSurfaceFactory that looks something like this:

interface IDIBSurfaceFactory
{
	HRESULT CreateBitmapSurface(BITMAPINFO *pbmi, void *pbits);
}

The BITMAPINO would contain the bitmapinfoheader and the color table. If pbits is NULL the surface would be allocated, if not then pbits itself would be used as the surface.

Compatibility Issues

This contains the compatibility issues with Front Page, Internet Explorer and anything else we need to document.

User Ed and PSS Issues

This section discusses things that are flags for User Ed or PSS e.g. user can remove bullet formatting by using outdent (not intuitive) etc. Not every spec will have items in this section.

Localization

PaulWu will fill in this section. Note any potential localization issues you think of when writing the spec.

What's New

Version 0.2

Added a few clarifications based on feedback from BrendanD and WHsu.

Version 0.3

Many changes from mmpowwow feedback:

Definitions

Tearing
Tearing is a display artifact caused when a blt operation traverses the same memory as the scan line output to the display. The visual effect is that one half of the displayed image is "pre-blt" the other half "post-blt." This effect is most visible when the blt moves through memory in the opposite direction of the scan line. This is a problem only when bltting directly to the display surface. It does not affect memory to memory blts that do not involve the primary display surface.