mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1877 lines
72 KiB
1877 lines
72 KiB
/*
|
|
* Copyright (c) 1993, 1995 Digital Equipment Corporation
|
|
*
|
|
* Module Name: Text.c
|
|
*
|
|
* This module contains routines to display text on the TGA card.
|
|
*
|
|
* TGA provides a mode to "stipple" data on the screen. In TRANSPARENT
|
|
* STIPPLE MODE, every "1" bit of the stipple mask is written to the frame
|
|
* buffer in the foreground color. "0" bits are unchanged. TGA also
|
|
* provides SOLID STIPPLE MODE where every "0" bit of the stipple mask is
|
|
* written to the frame buffer in the background color and every "1" bit is
|
|
* written in the foreground color. We use TRANSPARENT STIPPLE MODE since
|
|
* the opaque rectangle, if requested, is drawn using BLOCK FILL MODE which
|
|
* requires only a single write to the TGA for each scanline. Using SOLID
|
|
* STIPPLE MODE would make the code more complicated and require additional
|
|
* writes to the TGA.
|
|
*
|
|
* When GDI calls us to display a string, it provides us with a bitmap for
|
|
* each of the glyphs that are to be displayed. Unfortunately the glyph
|
|
* bitmaps are in a weird order, apparently something to do with the ordering
|
|
* that VGA devices prefer. TGA's stipple mask requires that the leftmost
|
|
* bit of the mask be the least-significant bit. To convert from the ordering
|
|
* that NT provides, we simply reverse each byte of the glyph bitmap.
|
|
*
|
|
* To improve performance, we cache the converted bitmap, along with other
|
|
* information we may need about the glyph. As we scan the string for
|
|
* glyphs to cache, we build a list of glyphs used in the string. This list
|
|
* contains a pointer to the cached information as well as information needed
|
|
* for each instance of each glyph.
|
|
*
|
|
* "stipple_text" is the routine that actually renders text to the screen.
|
|
* It scan down the list of glyphs in the string once for each scanline to
|
|
* build the stipple masks for the scanline. Once the appropriate scanline
|
|
* of each of the glyphs have been rendered into the scanline array, each of
|
|
* the stipple masks is sent to the TGA. Displaying the information on
|
|
* a scanline-by-scanline basis will (reportedly) allow the TGA to pipeline
|
|
* the most information.
|
|
*
|
|
* History:
|
|
*
|
|
* 21-Aug-1993 Barry Tannenbaum
|
|
* Removed *everything* from the QV driver so we can start from scratch.
|
|
*
|
|
* 19-Oct-1993 Barry Tannenbaum
|
|
* First pass code using the TGA. TGA code will only be enabled if the
|
|
* macro TGA_TEXT is defined
|
|
*
|
|
* 23-Oct-1993 Barry Tannenbaum
|
|
* Arbitrarily spaced and clipped text now works
|
|
*
|
|
* 28-Oct-1993 Barry Tannenbaum
|
|
* Fonts are now cached.
|
|
*
|
|
* 28-Oct-1993 Barry Tannenbaum
|
|
* Corrected write buffering errors.
|
|
*
|
|
* 1-Nov-1993 Barry Tannenbaum
|
|
* Switch from BLOCK FILL to OPAQUE FILL in fill_opaque. Either there's
|
|
* a problem with BLOCK FILL or we're not calling it right.
|
|
*
|
|
* 1-Nov-1993 Barry Tannenbaum
|
|
* Dan fixed the problem with BLOCK FILL mode so I removed the OPAQUE FILL
|
|
* hack. Dan had not initialized the ColumnSize bit in the DEEP register
|
|
* correctly.
|
|
*
|
|
* 2-Nov-1993 Barry Tannenbaum
|
|
* punt_text wasn't checking that the TGA was in simple mode.
|
|
*
|
|
* 2-Nov-1993 Barry Tannenbaum
|
|
* Fix clipping buglet
|
|
*
|
|
* 7-Nov-1993 Barry Tannenbaum
|
|
* Fixed error which set the pixel mask when there were no more bits to
|
|
* write in the stipple mask. This caused the *next* scanline to be
|
|
* clipped when it shouldn't have been.
|
|
*
|
|
* 8-Nov-1993 Barry Tannenbaum
|
|
* Punt text with overlapped glyphs.
|
|
*
|
|
* 8-Nov-1993 Barry Tannenbaum
|
|
* New scheme for handling register and framebuffer aliases
|
|
*
|
|
* 8-Nov-1993 Barry Tannenbaum
|
|
* Fixed clipping error when clipping on the right. We were forgetting
|
|
* to modify fragment_width. This resulted in us applying the pixel mask
|
|
* to the wrong data.
|
|
*
|
|
* 9-Nov-1993 Barry Tannenbaum
|
|
* Added CYCLE_REGS before TGAMODE in DrvTextOut. I don't know why but
|
|
* it fixes a bug when we're writing longwords to the TGA instead of
|
|
* unaligned data structures
|
|
*
|
|
* 10-Nov-1993 Barry Tannenbaum
|
|
* Added check for ppdev->bInPuntRoutine. Also added code to save, set
|
|
* and restore bInPuntRoutine in punt_paint.
|
|
*
|
|
* 11-Nov-1993 Bob Seitsinger
|
|
* Moved REVERSE_* macros to tgamacro.h.
|
|
*
|
|
* 12-Nov-1993 Barry Tannenbaum
|
|
* Fixed left edge clipping bugs for large glyphs with clipping beyond
|
|
* the first fragment.
|
|
*
|
|
* 13-Nov-1993 Barry Tannenbaum
|
|
* Fixed bugs skiping space when the left edge of the clipping rectangle
|
|
* falls in the space
|
|
*
|
|
* 10-Dec-1993 Barry Tannenbaum
|
|
* Convert to use punt surface
|
|
*
|
|
* 18-Jan-1994 Barry Tannenbaum
|
|
* Modified to accelerate overlapping glyphs
|
|
*
|
|
* 23-Jan-1994 Barry Tannenbaum
|
|
* Implemented suggestions from code review:
|
|
* - REVERSE_BYTE now uses a table instead of a macro.
|
|
* - Save information about glyphs in the string being rendered in a
|
|
* list. This way we don't have to calculate things like the glyph
|
|
* rectangle for each glyph every scanline.
|
|
* - Minor comment updates.
|
|
*
|
|
* 24-Jan-1994 Barry Tannenbaum
|
|
* Fixed error clipping on left edge
|
|
*
|
|
* 12-Feb-1994 Barry Tannenbaum
|
|
* Modified to allow to write to off-screen memory
|
|
*
|
|
* 22-Feb-1994 Barry Tannenbaum
|
|
* Use fill_solid in paint.c instead of private routine
|
|
*
|
|
* 3-Mar-1994 Barry Tannenbaum
|
|
* Moved 'reverse_byte' array to table.c so it can be shared with
|
|
* brush.c
|
|
*
|
|
* 21-Mar-1994 Bob Seitsinger
|
|
* Check # of clip objects returned from CLIPOBJ_bEnum before
|
|
* calling subroutine.
|
|
*
|
|
* 22-May-1994 Barry Tannenbaum
|
|
* Restored fill_opaque to this module. We must have crossed some
|
|
* locality threshold since the WinBench 4.0 text numbers dropped
|
|
* 10% after the last change to paint.c and were restored after this
|
|
* change.
|
|
*
|
|
* 21-Jul-1994 Bob Seitsinger
|
|
* Need to write to the plane mask register when using block fill
|
|
* mode.
|
|
*
|
|
* 9-Aug-1994 Barry Tannenbaum
|
|
* Setup for 24 plane support:
|
|
* - TGAMODE and TGAROP now take simple ULONGs instead of structures
|
|
* - Use default values from ppdev->ulModeTemplate & ppdev->ulRopTemplate
|
|
*
|
|
* 14-Aug-1994 Barry Tannenbaum
|
|
* Changes to support 24 plane board
|
|
*
|
|
* 31-Aug-1994 Barry Tannenbaum
|
|
* Mask high byte off of color in 24 plane mode
|
|
*
|
|
* 12-Sep-1994 Barry Tannenbaum
|
|
* Prevent writing off beginning of scanline when index is -1
|
|
*
|
|
* 14-Oct-1994 Tim Dziechowski
|
|
* Add stipple_text_trivial() to handle the 99.99% case of trivial (no)
|
|
* clipping. This incorporates speedups which double text throughput.
|
|
* Optimize expensive GDI *_enum calls out of cache_glyphs for cases
|
|
* when only one batch of glyphs is available.
|
|
* Unroll innermost loops in stipple_text_trivial. Add aliasing
|
|
* per glyph for safety, even though we get through HCT without it.
|
|
* Tweaks to new code for 24 bit support.
|
|
*
|
|
* 25-Oct-1994 Tim Dziechowski
|
|
* Remove aliasing from stipple_text_trivial; the bits may get written
|
|
* out in the wrong order for strings like "--", but they do get where
|
|
* they're supposed to go. Tweak the innerloop of stipple_text_trivial
|
|
* for _absolute_ minimal issues/stalls. Perf stat analysis reveals
|
|
* some surprises, so unroll the loop for narrow _aligned_ glyphs.
|
|
*
|
|
* 25-Oct-1994 Bob Seitsinger
|
|
* Write plane mask with ppdev->ulPlanemaskTemplate all the
|
|
* time.
|
|
*
|
|
* For 24 plane boards we don't want to blow away the
|
|
* windows ids for 3d windows. The GL driver removes the
|
|
* window ids when it relinquishes a rectangular area.
|
|
*
|
|
* 03-Nov-1994 Tim Dziechowski
|
|
* Stats support
|
|
*
|
|
* 12-Dec-1994 Barry Tannenbaum
|
|
* Fixed write buffering error in stipple_text_trivial.
|
|
*
|
|
* 2-Mar-1995 Barry Tannenbaum
|
|
* EV5 changes
|
|
*/
|
|
|
|
#include "driver.h"
|
|
#include "tgastats.h"
|
|
|
|
// Define DUMP_PUNTED_TEXT to write information about any punted strings
|
|
|
|
//#define DUMP_PUNTED_TEXT 1
|
|
|
|
// Definitions for cached glyph information
|
|
|
|
#define GLYPH_LIST_SIZE 256
|
|
|
|
#define HASH_HGLYPH(arg) ((int)arg % GLYPH_LIST_SIZE)
|
|
|
|
// Number of bits in a ULONG
|
|
|
|
#define ULONG_BITS 32
|
|
|
|
// Array used to "reverse" the bits of each byte in a glyph bitmap
|
|
|
|
extern ULONG reverse_byte[];
|
|
|
|
// Mask used to clip off unused bits on the right side of the glyph
|
|
|
|
static
|
|
ULONG long_clip[ULONG_BITS] =
|
|
{0xffffffff, // 11111111111111111111111111111111 0
|
|
0x7fffffff, // 01111111111111111111111111111111 1
|
|
0x3fffffff, // 00111111111111111111111111111111 2
|
|
0x1fffffff, // 00011111111111111111111111111111 3
|
|
0x0fffffff, // 00001111111111111111111111111111 4
|
|
0x07ffffff, // 00000111111111111111111111111111 5
|
|
0x03ffffff, // 00000011111111111111111111111111 6
|
|
0x01ffffff, // 00000001111111111111111111111111 7
|
|
0x00ffffff, // 00000000111111111111111111111111 8
|
|
0x007fffff, // 00000000011111111111111111111111 9
|
|
0x003fffff, // 00000000001111111111111111111111 10
|
|
0x001fffff, // 00000000000111111111111111111111 11
|
|
0x000fffff, // 00000000000011111111111111111111 12
|
|
0x0007ffff, // 00000000000001111111111111111111 13
|
|
0x0003ffff, // 00000000000000111111111111111111 14
|
|
0x0001ffff, // 00000000000000011111111111111111 15
|
|
0x0000ffff, // 00000000000000001111111111111111 16
|
|
0x00007fff, // 00000000000000000111111111111111 17
|
|
0x00003fff, // 00000000000000000011111111111111 18
|
|
0x00001fff, // 00000000000000000001111111111111 19
|
|
0x00000fff, // 00000000000000000000111111111111 20
|
|
0x000007ff, // 00000000000000000000011111111111 21
|
|
0x000003ff, // 00000000000000000000001111111111 22
|
|
0x000001ff, // 00000000000000000000000111111111 23
|
|
0x000000ff, // 00000000000000000000000011111111 24
|
|
0x0000007f, // 00000000000000000000000001111111 25
|
|
0x0000003f, // 00000000000000000000000000111111 26
|
|
0x0000001f, // 00000000000000000000000000011111 27
|
|
0x0000000f, // 00000000000000000000000000001111 28
|
|
0x00000007, // 00000000000000000000000000000111 29
|
|
0x00000003, // 00000000000000000000000000000011 30
|
|
0x00000001}; // 00000000000000000000000000000001 31
|
|
|
|
/*
|
|
* bInitText
|
|
*
|
|
* This routine is called to initialize the text routines. It allocates memory
|
|
* used to build the scanline. If the memory allocation fails, the routine
|
|
* returns FALSE.
|
|
*/
|
|
|
|
BOOL bInitText (PPDEV ppdev)
|
|
{
|
|
ppdev->ulScanlineBytes = (ppdev->lScreenStride + // Maximum scanline width
|
|
31) & ~31; // Round to DWORDS
|
|
|
|
ppdev->ulScanlineBytes /= 8; // 8 bits per byte
|
|
|
|
ppdev->ulScanline = EngAllocMem (0, ppdev->ulScanlineBytes, ALLOC_TAG);
|
|
|
|
return (ppdev->ulScanline != NULL);
|
|
}
|
|
|
|
/*
|
|
* vTermText
|
|
*
|
|
* This routine is called to return any resources allocated by the text
|
|
* routines.
|
|
*/
|
|
|
|
VOID vTermText (PPDEV ppdev)
|
|
{
|
|
|
|
if (NULL != ppdev->ulScanline)
|
|
EngFreeMem (ppdev->ulScanline);
|
|
ppdev->ulScanline = NULL;
|
|
|
|
if (NULL != ppdev->pGlyphList)
|
|
EngFreeMem (ppdev->pGlyphList);
|
|
ppdev->pGlyphList = NULL;
|
|
ppdev->ulGlyphListCount = 0;
|
|
}
|
|
|
|
/*
|
|
* stipple_text_trivial
|
|
*
|
|
* This routine writes text to the framebuffer using TGA's stipple mode.
|
|
* Text is stippled onto the display glyph-by-glyph.
|
|
*
|
|
* Since there is no clipping for the DC_TRIVIAL case which calls this,
|
|
* we can eliminate all of the clip tests for an additional speed boost.
|
|
*
|
|
* 99+% of all glyphs come through here.
|
|
*
|
|
* Most glyphs output by normal Windows apps are small. Here is a breakdown
|
|
* by width and height of all the glyphs output by the WinBench 4.0 Graphics
|
|
* WinMark text tests (GWM 5, 6, and 7), and by our WinPerf native AXP test:
|
|
*
|
|
* WINBENCH GWM WINPERF NATIVE AXP
|
|
*
|
|
* PIXELS WIDTH HEIGHT WIDTH HEIGHT
|
|
*
|
|
* 0 0 0 0 0
|
|
* 1 33708 33163 0 0
|
|
* 2 7420 11 195205 0
|
|
* 3 76052 2140 663000 0
|
|
* 4 83987 1300 117168 0
|
|
* 5 47297 22765 312167 0
|
|
* 6 66229 51269 1365350 0
|
|
* 7 75885 26110 546464 0
|
|
* 8 53558 26005 429821 0
|
|
* 9 17013 47643 39128 0
|
|
* 10 5358 2154 35 0
|
|
* 11 5525 16895 78059 0
|
|
* 12 1612 464 45 0
|
|
* 13 1713 125091 0 3745666
|
|
* 14 558 223 8 0
|
|
* 15 312 19 0 0
|
|
* 16 0 120969 0 784
|
|
* 17 0 0 0 0
|
|
* 18 0 0 0 0
|
|
* 19 0 0 0 0
|
|
* 20 0 0 0 0
|
|
*
|
|
* There is some minor noise in the stats above due to GUI TextOuts
|
|
* processed by the driver while getting through the tests. Stats
|
|
* above are for 1024x768. This confirms the design choice of 16
|
|
* for a loop unroll size.
|
|
*
|
|
* The real surprise is that aligned glyphs (0 == left_offset) account
|
|
* for a much higher percentage of Windows glyphs than expected.
|
|
*
|
|
* WINBENCH GWM WINPERF NATIVE AXP
|
|
*
|
|
* ALIGNED 117020 1031249
|
|
* UNALIGNED 359210 2715201
|
|
*
|
|
* Therefore the loop for narrow aligned glyphs has been unrolled too.
|
|
*/
|
|
|
|
void stipple_text_trivial (SURFOBJ *pso,
|
|
STROBJ *pstro,
|
|
CACHED_GLYPH_INFO *cached_glyphs[])
|
|
{
|
|
ULONG glyph_bits;
|
|
ULONG glyph_bits2;
|
|
ULONG *pglyph_bits;
|
|
ULONG *pglyph_bits2;
|
|
ULONG last_glyph_bits;
|
|
ULONG stipple_data;
|
|
ULONG stipple_data2;
|
|
int i, x, y; // Counters for glyph, glyph bits & scanline
|
|
int width, height; // Native glyph dimensions, fastest for loops
|
|
int unroll;
|
|
int left_pixel; // Aligned left-most pixel for clipping rect.
|
|
ULONG left_offset; // Shift for left bits, current glyph fragment
|
|
ULONG right_offset; // " right "
|
|
GLYPH_LIST *glyph; // Saved information for current glyph
|
|
PPDEV ppdev = (PPDEV) pso->dhpdev;
|
|
PBYTE pdisp, pdisp2;
|
|
ULONG stride, stride_2x;
|
|
ULONG bytes_per_pixel;
|
|
ULONG address_increment;
|
|
ULONG x_displacement;
|
|
|
|
pdisp = SURFOBJ_base_address (pso);
|
|
stride = SURFOBJ_stride (pso);
|
|
stride_2x = stride << 1;
|
|
bytes_per_pixel = SURFOBJ_bytes_per_pixel (pso);
|
|
address_increment = bytes_per_pixel * ULONG_BITS;
|
|
|
|
// Write glyph data to display for all glyphs in the string.
|
|
|
|
for (i = 0; i < ppdev->ulGlyphCount; i++)
|
|
{
|
|
glyph = &ppdev->pGlyphList[i];
|
|
pglyph_bits2 = pglyph_bits = glyph->cached_info->bitmap;
|
|
width = glyph->cached_info->size.cx;
|
|
height = glyph->cached_info->size.cy;
|
|
|
|
// Calculate the leftmost pixel for this glyph rectangle. We have
|
|
// to allow for alignment to a 4-pixel boundry.
|
|
|
|
left_pixel = glyph->rect.left & ~0x03;
|
|
|
|
// Compute the start framebuffer address for this glyph
|
|
|
|
pdisp += glyph->rect.top * stride +
|
|
(ULONG)(left_pixel * bytes_per_pixel); // dual issue mull/muls
|
|
|
|
// Compute the glyph's offset. If offset == 0, then the glyph
|
|
// fragment exactly lines up with the DWORDs we are writing to
|
|
// the framebuffer. Otherwise the fragment straddles two DWORDs,
|
|
// so we have to bitshift and possibly write multiple DWORDs.
|
|
|
|
left_offset = glyph->rect.left - left_pixel;
|
|
right_offset = 32 - left_offset;
|
|
|
|
// Narrow glyphs, don't have to loop over x.
|
|
if (width <= ULONG_BITS)
|
|
{
|
|
if (0 == left_offset) // Aligned narrow glyph...easiest
|
|
{
|
|
pdisp2 = pdisp; // we'll use this for alternate writes
|
|
pdisp -= stride_2x; // point to . - 2
|
|
pdisp2 -= stride; // point to . - 1
|
|
pglyph_bits2++; // point to second of pair
|
|
|
|
// For all y scanlines in this narrow glyph...
|
|
for (y = height; y > 0 ;)
|
|
{
|
|
unroll = min((int)y, 16);
|
|
y -= unroll;
|
|
if (unroll & 1)
|
|
{
|
|
switch(unroll)
|
|
{
|
|
case 15:
|
|
pdisp += stride_2x; // now points to .0
|
|
pdisp2 += stride_2x; // now points to .1
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2);
|
|
case 13:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2);
|
|
case 11:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2);
|
|
case 9:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2);
|
|
case 7:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2);
|
|
case 5:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2);
|
|
case 3:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2);
|
|
case 1:
|
|
pdisp += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
pglyph_bits += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch(unroll)
|
|
{
|
|
case 16:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2);
|
|
case 14:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2);
|
|
case 12:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2);
|
|
case 10:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2);
|
|
case 8:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2);
|
|
case 6:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2);
|
|
case 4:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2);
|
|
case 2:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (width <= (int)right_offset)
|
|
{
|
|
// The glyph is unaligned but narrow enough that we don't
|
|
// need to worry about right offset. The majority of glyphs
|
|
// hit this code, so we unroll the innermost loop with a
|
|
// stacked switch. This unroll bought us .5 Mpix/sec.
|
|
|
|
pdisp2 = pdisp; // we'll use this for alternate writes
|
|
pdisp -= stride_2x; // point to . - 2
|
|
pdisp2 -= stride; // point to . - 1
|
|
pglyph_bits2++; // point to second of pair
|
|
|
|
for (y = height; y > 0 ; )
|
|
{
|
|
unroll = min((int)y, 16);
|
|
y -= unroll;
|
|
|
|
// This bit with the odd-even switches is to make
|
|
// the basic block for each switch case big enough
|
|
// to minimize pipeline stalls. This code gives
|
|
// us 1.5 stalls per TGAWRITE versus 4 with a
|
|
// conventional stacked switch. The second set of
|
|
// independent variables for the second write in each
|
|
// case also minimizes latency and helps CLAXP schedule.
|
|
|
|
if (unroll & 1)
|
|
{
|
|
switch(unroll)
|
|
{
|
|
case 15:
|
|
pdisp += stride_2x; // now points to .0
|
|
pdisp2 += stride_2x; // now points to .1
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits << left_offset);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2 << left_offset);
|
|
case 13:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits << left_offset);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2 << left_offset);
|
|
case 11:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits << left_offset);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2 << left_offset);
|
|
case 9:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits << left_offset);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2 << left_offset);
|
|
case 7:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits << left_offset);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2 << left_offset);
|
|
case 5:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits << left_offset);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2 << left_offset);
|
|
case 3:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits << left_offset);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2 << left_offset);
|
|
case 1:
|
|
pdisp += stride_2x;
|
|
glyph_bits = *pglyph_bits++;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits << left_offset);
|
|
break;
|
|
} // switch unroll
|
|
}
|
|
else
|
|
{
|
|
switch(unroll)
|
|
{
|
|
case 16:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits << left_offset);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2 << left_offset);
|
|
case 14:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits << left_offset);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2 << left_offset);
|
|
case 12:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits << left_offset);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2 << left_offset);
|
|
case 10:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits << left_offset);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2 << left_offset);
|
|
case 8:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits << left_offset);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2 << left_offset);
|
|
case 6:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits << left_offset);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2 << left_offset);
|
|
case 4:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits << left_offset);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2 << left_offset);
|
|
case 2:
|
|
pdisp += stride_2x;
|
|
pdisp2 += stride_2x;
|
|
glyph_bits = *pglyph_bits;
|
|
glyph_bits2 = *pglyph_bits2;
|
|
pglyph_bits += 2;
|
|
pglyph_bits2 += 2;
|
|
if (0 != glyph_bits)
|
|
TGAWRITE (ppdev, pdisp, glyph_bits << left_offset);
|
|
if (0 != glyph_bits2)
|
|
TGAWRITE (ppdev, pdisp2, glyph_bits2 << left_offset);
|
|
break;
|
|
} // switch unroll
|
|
}
|
|
} // all ylines
|
|
} // commonest narrow glyph case
|
|
else
|
|
{
|
|
// Have to do two writes per xline
|
|
for (y = height; y > 0 ; y--, pdisp += stride)
|
|
{
|
|
glyph_bits = *pglyph_bits++;
|
|
stipple_data = glyph_bits << left_offset;
|
|
stipple_data2 = glyph_bits >> right_offset;
|
|
if (0 != stipple_data)
|
|
{
|
|
TGAWRITE (ppdev, pdisp, stipple_data);
|
|
}
|
|
if (0 != stipple_data2)
|
|
{
|
|
TGAWRITE (ppdev, pdisp + address_increment, stipple_data2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else // The glyph is > 32 pixels wide, must loop over x
|
|
{
|
|
if (0 == left_offset) // Aligned wide glyphs
|
|
{
|
|
// For all y scanlines in this wide glyph...
|
|
for (y = height; y > 0 ; y--, pdisp += stride)
|
|
{
|
|
// For all glyph bits in this scanline...
|
|
x_displacement = 0;
|
|
for (x = width; x > 0; x -= ULONG_BITS)
|
|
{
|
|
glyph_bits = *pglyph_bits++;
|
|
if (0 != glyph_bits)
|
|
{
|
|
TGAWRITE (ppdev, pdisp + x_displacement, glyph_bits);
|
|
}
|
|
x_displacement += address_increment;
|
|
}
|
|
}
|
|
}
|
|
else // Offset nonzero, nonaligned wide glyphs
|
|
{
|
|
// For all y scanlines in this wide glyph...
|
|
for (y = height; y > 0 ; y--, pdisp += stride)
|
|
{
|
|
// For all glyph bits in this scanline...
|
|
x_displacement = 0;
|
|
last_glyph_bits = 0;
|
|
for (x = width; x > 0; x -= ULONG_BITS)
|
|
{
|
|
glyph_bits = *pglyph_bits++;
|
|
stipple_data = (last_glyph_bits >> right_offset) |
|
|
(glyph_bits << left_offset);
|
|
last_glyph_bits = glyph_bits;
|
|
if (0 != stipple_data)
|
|
{
|
|
TGAWRITE (ppdev, pdisp + x_displacement, stipple_data);
|
|
}
|
|
x_displacement += address_increment;
|
|
}
|
|
// The last ULONG of each xline may need another write
|
|
if (x + left_offset >= 0)
|
|
{
|
|
stipple_data = last_glyph_bits >> right_offset;
|
|
if (0 != stipple_data)
|
|
{
|
|
TGAWRITE (ppdev, pdisp + x_displacement, stipple_data);
|
|
}
|
|
}
|
|
} // for all y scanlines in this glyph
|
|
} // aligned/nonaligned wide glyphs
|
|
} // wide/narrow glyphs
|
|
|
|
pdisp = CYCLE_FB (ppdev);
|
|
|
|
} // all glyphs in string
|
|
}
|
|
|
|
/*
|
|
* stipple_text
|
|
*
|
|
* This routine writes text to the framebuffer using TGA's stipple mode.
|
|
* Text is stippled onto the display scanline by scanline to take advantage
|
|
* of the pipelining in the TGA chip.
|
|
*
|
|
* The routine assembles a series of long's (32 bits) of a scanline of the
|
|
* text from the rasterized glyphs provided by GDI which we've carefully
|
|
* cached *before* calling this routine.
|
|
*/
|
|
|
|
//static
|
|
void stipple_text (SURFOBJ *pso,
|
|
STROBJ *pstro,
|
|
RECTL *clip_rect,
|
|
CACHED_GLYPH_INFO *cached_glyphs[])
|
|
{
|
|
ULONG glyph_bits; // Contents of cell in glyph bitmap
|
|
int i, x, y; // Counters for glyph, glyph bits & scanline
|
|
int fragment_width; // Width in bits of the piece of the glyph
|
|
int min_x, max_x; // Min/Max X values filled for scanline
|
|
int glyph_scanline; // Line of the current glyph
|
|
ULONG *scanline; // Long pointer to scanline memory
|
|
int left_pixel; // Aligned left-most pixel for clipping rect.
|
|
int scanline_index; // Index into scanline array for current glyph
|
|
int scanline_offset;// Pixel shift for current glyph
|
|
int index, offset; // Index and shift for current glyph fragment
|
|
int left_glyph_pixel;// Left-most pixel for current glyph fragment
|
|
GLYPH_LIST *glyph; // Saved information for current glyph
|
|
int min_glyph; // Minimum index into saved glyph information
|
|
int max_glyph; // Maximum index into saved glyph information
|
|
PPDEV ppdev = (PPDEV) pso->dhpdev;
|
|
PBYTE base_address;
|
|
ULONG stride;
|
|
ULONG bytes_per_pixel;
|
|
PBYTE address; // Framebuffer address we're writing to
|
|
PBYTE min_address, max_address;
|
|
int min_offset, span;
|
|
|
|
base_address = SURFOBJ_base_address (pso);
|
|
stride = SURFOBJ_stride (pso);
|
|
bytes_per_pixel = SURFOBJ_bytes_per_pixel (pso);
|
|
|
|
// Calculate the leftmost pixel for this glyph rectangle. We have to allow
|
|
// for alignment to a 4-pixel boundry
|
|
|
|
left_pixel = clip_rect->left & ~0x03; // Round down to 4-byte boundry
|
|
|
|
// Initialize the glyph list that we allocated when we checked that each
|
|
// glyph was cached. We have to do this now instead of when we cache the
|
|
// glyphs so that we can handle passes with multiple clipping rectangles
|
|
|
|
min_glyph = ppdev->ulGlyphCount;
|
|
max_glyph = -1;
|
|
|
|
for (i = 0; i < ppdev->ulGlyphCount; i++)
|
|
{
|
|
glyph = &ppdev->pGlyphList[i];
|
|
|
|
// Initialize glyph->bitmap to point to the first scanline of the
|
|
// cached glyph bitmap that we're going to use when rendering this
|
|
// glyph
|
|
|
|
if (clip_rect->top <= glyph->rect.top)
|
|
glyph->bitmap = glyph->cached_info->bitmap;
|
|
else
|
|
glyph->bitmap = glyph->cached_info->bitmap +
|
|
(glyph->cached_info->stride_in_longs *
|
|
(clip_rect->top - glyph->rect.top));
|
|
|
|
// Try to limit the number of glyphs that we scan by not considering
|
|
// glyphs outside the clipping rectangle
|
|
|
|
if ((glyph->rect.left <= clip_rect->right) &&
|
|
(glyph->rect.right >= clip_rect->left))
|
|
{
|
|
min_glyph = MIN (i, min_glyph);
|
|
max_glyph = MAX (i, max_glyph);
|
|
|
|
// Figure out where the glyph falls in the scanline array
|
|
|
|
if (glyph->rect.left >= left_pixel)
|
|
{
|
|
glyph->index = (glyph->rect.left - left_pixel) / ULONG_BITS;
|
|
glyph->offset = (glyph->rect.left - left_pixel) % ULONG_BITS;
|
|
}
|
|
else
|
|
{
|
|
glyph->index = (glyph->rect.left - left_pixel) / ULONG_BITS;
|
|
glyph->index--;
|
|
glyph->offset = (glyph->rect.left - left_pixel) % ULONG_BITS;
|
|
glyph->offset += ULONG_BITS;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Grab the pointer to the scanline array memory allocated by bInitText
|
|
|
|
scanline = ppdev->ulScanline;
|
|
|
|
// Stipple the text onto the framebuffer scanline by scanline. This will
|
|
// (reportedly) get the best performance out of the TGA since it allows
|
|
// the TGA to pipeline the most information
|
|
|
|
for (y = 0; y < clip_rect->bottom - clip_rect->top; y++)
|
|
{
|
|
// Initialize for this pass through the glyphs
|
|
|
|
memset (scanline, 0, ppdev->ulScanlineBytes);
|
|
min_x = clip_rect->right + 1;
|
|
max_x = -1;
|
|
|
|
// Scan through the list of glyphs for glyphs which are rendered on
|
|
// this scanline
|
|
|
|
for (i = min_glyph; i <= max_glyph; i++)
|
|
{
|
|
glyph = &ppdev->pGlyphList[i];
|
|
|
|
// Skip glyphs with no bits on this scanline
|
|
|
|
glyph_scanline = (clip_rect->top + y) - glyph->rect.top;
|
|
if ((glyph_scanline < 0) ||
|
|
(glyph_scanline >= glyph->cached_info->size.cy))
|
|
continue;
|
|
|
|
// Check for glyphs outside the clip region. If we've found one,
|
|
// skip to the next glyph
|
|
|
|
if ((glyph->rect.left >= clip_rect->right) ||
|
|
(glyph->rect.right < clip_rect->left))
|
|
continue;
|
|
|
|
// OR each DWORD of the line for this glyph into the scanline
|
|
|
|
scanline_index = glyph->index;
|
|
scanline_offset = glyph->offset;
|
|
|
|
for (x = glyph->cached_info->size.cx; x > 0; x -= ULONG_BITS)
|
|
{
|
|
index = scanline_index;
|
|
offset = scanline_offset;
|
|
|
|
left_glyph_pixel = glyph->rect.left + (glyph->cached_info->size.cx - x);
|
|
|
|
fragment_width = MIN (x, ULONG_BITS);
|
|
glyph_bits = *glyph->bitmap;
|
|
|
|
// If there's no information, skip to the next fragment
|
|
|
|
if (0 == glyph_bits)
|
|
goto next_fragment;
|
|
|
|
// Clip on the left edge, if necessary.
|
|
|
|
if (left_glyph_pixel < clip_rect->left)
|
|
{
|
|
int clip_width;
|
|
|
|
clip_width = clip_rect->left - left_glyph_pixel;
|
|
if (clip_width >= ULONG_BITS)
|
|
goto next_fragment;
|
|
|
|
glyph_bits = glyph_bits >> clip_width;
|
|
fragment_width -= clip_width;
|
|
left_glyph_pixel += clip_width;
|
|
|
|
offset += clip_width;
|
|
if (offset >= ULONG_BITS)
|
|
{
|
|
offset -= ULONG_BITS;
|
|
index++;
|
|
}
|
|
}
|
|
|
|
// Clip on the right edge, if necessary. If we clip,
|
|
// force x to 0 so that we'll stop processing this glyph's
|
|
// bitmap
|
|
|
|
if (clip_rect->right < (left_glyph_pixel + fragment_width))
|
|
{
|
|
int clip_width;
|
|
|
|
glyph->bitmap += (x - 1) / ULONG_BITS;
|
|
x = 0;
|
|
|
|
clip_width = (left_glyph_pixel + fragment_width) -
|
|
clip_rect->right +
|
|
(ULONG_BITS - fragment_width);
|
|
|
|
if (clip_width >= ULONG_BITS)
|
|
goto next_fragment;
|
|
|
|
glyph_bits = glyph_bits & long_clip[clip_width];
|
|
fragment_width = ULONG_BITS - clip_width;
|
|
}
|
|
|
|
// Add the glyph bits to the scanline. Don't bother if
|
|
// there aren't any bits to add
|
|
|
|
if (0 != glyph_bits)
|
|
{
|
|
min_x = MIN (min_x, left_glyph_pixel);
|
|
max_x = MAX (max_x, (left_glyph_pixel + fragment_width));
|
|
|
|
// If scanline_offset == 0, then the glyph fragment
|
|
// exactly lines up with the DWORDs that are used to
|
|
// access the scanline. Otherwise the fragment
|
|
// straddles two DWORDs, so we have to store part in
|
|
// each of the DWORDs.
|
|
|
|
if (0 == offset)
|
|
scanline[index] |= glyph_bits;
|
|
else
|
|
{
|
|
if (index < 0)
|
|
{
|
|
if (-1 == index)
|
|
scanline[0] |=
|
|
glyph_bits >> (ULONG_BITS - offset);
|
|
else
|
|
DISPDBG ((0, "Scanline index = %d\n", index));
|
|
}
|
|
else
|
|
{
|
|
scanline[index] |= glyph_bits << offset;
|
|
scanline[index+1] |=
|
|
glyph_bits >> (ULONG_BITS - offset);
|
|
}
|
|
}
|
|
}
|
|
|
|
next_fragment:
|
|
scanline_index++;
|
|
glyph->bitmap++;
|
|
|
|
} // for each glyph fragment
|
|
|
|
} // for each glyph in this string
|
|
|
|
// Write out the scanline we've accumulated
|
|
|
|
if (max_x > min_x)
|
|
{
|
|
span = max_x - min_x;
|
|
min_offset = (min_x - left_pixel) & ~31;
|
|
span += (min_x - left_pixel) & 31;
|
|
|
|
min_address = base_address +
|
|
((clip_rect->top + y) * stride) +
|
|
(left_pixel + min_offset) * bytes_per_pixel;
|
|
max_address = min_address + (span * bytes_per_pixel);
|
|
scanline_index = min_offset / ULONG_BITS;
|
|
|
|
for (address = min_address;
|
|
address < max_address;
|
|
address += ULONG_BITS * bytes_per_pixel)
|
|
{
|
|
if (0 != scanline[scanline_index])
|
|
TGAWRITE (ppdev, address, scanline[scanline_index]);
|
|
scanline_index++;
|
|
}
|
|
}
|
|
|
|
} // For each scanline
|
|
|
|
}
|
|
|
|
/*
|
|
* do_opaque
|
|
*
|
|
* This routine actually fills the opaque rectangle. It assumes that
|
|
* it's been given clipped rectangles.
|
|
*/
|
|
|
|
//static
|
|
//__inline
|
|
VOID do_opaque (SURFOBJ *pso, // Surface to fill
|
|
RECTL *rect) // Rectangle to be filled
|
|
{
|
|
PBYTE left_edge; // Framebuffer address of left edge of rectangle
|
|
int y; // Counts number of scanlines written
|
|
int width; // Width of the opaque rectangle
|
|
int align_pixels; // Pixels shifted to align
|
|
PPDEV ppdev = // Device context
|
|
(PPDEV) pso->dhpdev;
|
|
ULONG stride; // Width of destination
|
|
|
|
// Fill each line of the rectangle. In block mode we can fill up to 2048
|
|
// pixels each write. Since none of the displays we're considering are
|
|
// wider than 1280 pixels/scanline, it should be safe to ignore considering
|
|
// whether we need more than 1 write/scanline
|
|
|
|
stride = SURFOBJ_stride (pso);
|
|
left_edge = SURFOBJ_base_address (pso) +
|
|
(rect->top * stride) +
|
|
(rect->left * SURFOBJ_bytes_per_pixel (pso));
|
|
|
|
// Calculate the alignment pixels and subtract them from the left edge to
|
|
// align to a 4-pixel boundry
|
|
|
|
align_pixels = (unsigned int)left_edge & 0x03;
|
|
left_edge = left_edge - align_pixels;
|
|
|
|
// Remember that the width does *not* include the right edge, so subtract
|
|
// one
|
|
|
|
width = (rect->right - rect->left) - 1;
|
|
|
|
// OR in the low bits of the address (the alignment pixels) into the upper
|
|
// word, since that's where BLOCK FILL mode looks for them
|
|
|
|
width |= (align_pixels << 16);
|
|
|
|
// Fill each scanline
|
|
|
|
for (y = 0; y < rect->bottom - rect->top; y ++)
|
|
{
|
|
TGAWRITE (ppdev, left_edge, width);
|
|
left_edge += stride;
|
|
}
|
|
|
|
CYCLE_FB (ppdev);
|
|
}
|
|
|
|
/*
|
|
* fill_opaque
|
|
*
|
|
* This routine paints one or more rectangles with a solid color
|
|
*/
|
|
|
|
//static
|
|
VOID fill_opaque (SURFOBJ *pso, // Surface to fill
|
|
RECTL *fill_rect, // Rectangle to fill
|
|
CLIPOBJ *pco, // Clip List
|
|
ULONG color) // Color to fill rectangle with
|
|
{
|
|
ULONG mode; // TGA mode register
|
|
ULONG rop; // Assembles TGA raster op register
|
|
BOOL more_rects; // Flags whether more rectangles to fill
|
|
PPDEV ppdev = // Device context block
|
|
(PPDEV) pso->dhpdev;
|
|
RECTL clip_rect; // Clipped rectangle we're to fill
|
|
ENUMRECTS cur_rect;
|
|
|
|
TGAPLANEMASK (ppdev, ppdev->ulPlanemaskTemplate);
|
|
|
|
// Set the mode and raster op registers
|
|
|
|
mode = ppdev->ulModeTemplate | TGA_MODE_BLOCK_FILL;
|
|
TGAMODE (ppdev, mode);
|
|
|
|
rop = ppdev->ulRopTemplate | TGA_ROP_COPY;
|
|
TGAROP (ppdev, rop);
|
|
|
|
TGADATA (ppdev, 0xffffffff); // Write to all 32 pixels
|
|
|
|
// Load the BLK_COLOR registers
|
|
|
|
if (BMF_8BPP == SURFOBJ_format (pso))
|
|
{
|
|
color = color | (color << 8);
|
|
color |= color << 16;
|
|
TGACOLOR0 (ppdev, color);
|
|
TGACOLOR1 (ppdev, color);
|
|
}
|
|
else
|
|
{
|
|
TGACOLOR0 (ppdev, color); // Store color in all 8 color registers
|
|
TGACOLOR1 (ppdev, color);
|
|
TGACOLOR2 (ppdev, color);
|
|
TGACOLOR3 (ppdev, color);
|
|
TGACOLOR4 (ppdev, color);
|
|
TGACOLOR5 (ppdev, color);
|
|
TGACOLOR6 (ppdev, color);
|
|
TGACOLOR7 (ppdev, color);
|
|
}
|
|
|
|
CYCLE_REGS (ppdev);
|
|
|
|
// Fill the assorted clipping rectangles
|
|
|
|
switch (pco->iDComplexity)
|
|
{
|
|
case DC_TRIVIAL:
|
|
do_opaque (pso, fill_rect);
|
|
return;
|
|
|
|
case DC_RECT:
|
|
if (bIntersectRects (&clip_rect, &pco->rclBounds, fill_rect))
|
|
do_opaque (pso, &clip_rect);
|
|
return;
|
|
|
|
case DC_COMPLEX:
|
|
CLIPOBJ_cEnumStart (pco, FALSE, CT_RECTANGLES, CD_ANY, 0);
|
|
do
|
|
{
|
|
more_rects = CLIPOBJ_bEnum (pco, sizeof(cur_rect),
|
|
(ULONG *)&cur_rect);
|
|
if (cur_rect.c > 0)
|
|
{
|
|
if (bIntersectRects (&clip_rect, &cur_rect.arcl[0], fill_rect))
|
|
do_opaque (pso, &clip_rect);
|
|
}
|
|
} while (more_rects);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* store_glyph
|
|
*
|
|
* This routine saves information about a glyph that we'll want later. Most
|
|
* importantly, it swaps the bits of the glyph so that the least significant
|
|
* bit of each glyph scanline is the leftmost bit.
|
|
*/
|
|
|
|
//static
|
|
CACHED_GLYPH_INFO *store_glyph (CACHED_GLYPH_INFO *next_glyph,
|
|
GLYPHPOS *pgp)
|
|
{
|
|
CACHED_GLYPH_INFO *new_glyph;
|
|
ULONG bitmap_bytes;
|
|
GLYPHBITS *pgb;
|
|
ULONG stride_in_bytes;
|
|
LONG x, y;
|
|
BYTE *src, *dest;
|
|
|
|
pgb = pgp->pgdf->pgb;
|
|
|
|
// Allocate a CACHED_GLYPH_INFO structure and fill it in
|
|
|
|
new_glyph = EngAllocMem (FL_ZERO_MEMORY, sizeof(CACHED_GLYPH_INFO), ALLOC_TAG);
|
|
if (NULL == new_glyph)
|
|
return NULL;
|
|
|
|
new_glyph->next = next_glyph;
|
|
new_glyph->hg = pgp->hg;
|
|
new_glyph->stride_in_longs = (pgb->sizlBitmap.cx + 31) / 32;
|
|
new_glyph->size = pgb->sizlBitmap;
|
|
stride_in_bytes = new_glyph->stride_in_longs * sizeof(ULONG);
|
|
|
|
// Allocate space for the glyph bitmap
|
|
|
|
bitmap_bytes = new_glyph->stride_in_longs * pgb->sizlBitmap.cy *
|
|
sizeof (ULONG);
|
|
new_glyph->bitmap = EngAllocMem (FL_ZERO_MEMORY, bitmap_bytes, ALLOC_TAG);
|
|
if (NULL == new_glyph->bitmap)
|
|
{
|
|
EngFreeMem (new_glyph);
|
|
return NULL;
|
|
}
|
|
|
|
// Copy the bitmap. We're expanding everything to longs, so "dest" may
|
|
// have to jump a few bytes between scanlines. Expanding to longs means
|
|
// that we don't have to do byte manipulations when we build the scanline.
|
|
// Since Alpha doesn't do byte manipulations quickly, this is a win.
|
|
//
|
|
// As we copy each byte, we reverse it end-for-end; high bit becomes low
|
|
// bit, etc. Why do we need to swap? Glad you asked. NT gives us the
|
|
// glyph bitmaps in a format ready to blt. For blt's the high bit of the
|
|
// glyph is the leftmost bit on the screen. TGA provides a mechanism to
|
|
// expand this 2-deep bitmap to the required colors called stippling. For
|
|
// the stipple mask, the low bit of the mask is the leftmost bit on the
|
|
// screen.
|
|
//
|
|
// We reverse all of the glyph bitmaps when we cache them, instead of
|
|
// reversing the stipple masks after we accumulate them to save time.
|
|
|
|
src = (BYTE *)pgb->aj;
|
|
for (y = 0; y < pgb->sizlBitmap.cy; y++)
|
|
{
|
|
dest = (BYTE *)new_glyph->bitmap + (y * stride_in_bytes);
|
|
|
|
for (x = pgb->sizlBitmap.cx; x > 0; x -= 8)
|
|
*dest++ = (BYTE)reverse_byte[*src++];
|
|
}
|
|
|
|
return new_glyph;
|
|
}
|
|
|
|
/*
|
|
* cache_glyphs
|
|
*
|
|
* This routine scans the string for any glyphs that are not cached and adds
|
|
* them to the cache.
|
|
*/
|
|
|
|
//static
|
|
BOOL cache_glyphs (PPDEV ppdev,
|
|
STROBJ *pstro,
|
|
CACHED_GLYPH_INFO *cached_glyphs[GLYPH_LIST_SIZE])
|
|
{
|
|
BOOL more_glyphs; // Flags whether there are more glyph arrays to process
|
|
BOOL enum_glyphs; // Flags whether we need to enumerate glyphs
|
|
int cGlyphs; // Counter of glyphs in the current STROBJ
|
|
GLYPHPOS *GlyphList; // Array of glyphs for the current STROBJ
|
|
GLYPHBITS *pgb; // Glyph definition for the current glyph
|
|
int i; // Index into array of glyphs
|
|
int hashed_index; // Index into array of cached glyphs
|
|
CACHED_GLYPH_INFO
|
|
*prev_glyph, // Cached glyphs. Used to walk the chain of
|
|
*cur_glyph, // glyphs when we have a hash collision.
|
|
*new_glyph;
|
|
GLYPH_LIST *glyph;
|
|
ULONG glyph_count; // Index into GLYPH_LIST array
|
|
|
|
// Count the number of glyphs in this string
|
|
|
|
glyph_count = 0;
|
|
if (pstro->pgp != NULL)
|
|
{
|
|
// There's only the one batch of glyphs, so save ourselves
|
|
// a call:
|
|
GlyphList = pstro->pgp;
|
|
glyph_count = cGlyphs = pstro->cGlyphs;
|
|
more_glyphs = FALSE;
|
|
enum_glyphs = FALSE;
|
|
}
|
|
else
|
|
{
|
|
enum_glyphs = TRUE;
|
|
do
|
|
{
|
|
more_glyphs = STROBJ_bEnum (pstro, &cGlyphs, &GlyphList);
|
|
glyph_count += cGlyphs;
|
|
}
|
|
while (more_glyphs);
|
|
}
|
|
|
|
ppdev->ulGlyphCount = glyph_count;
|
|
|
|
// If needed expand the list of saved information for the glyphs in this
|
|
// string
|
|
|
|
if (ppdev->ulGlyphListCount < glyph_count)
|
|
{
|
|
if (NULL != ppdev->pGlyphList)
|
|
EngFreeMem (ppdev->pGlyphList);
|
|
ppdev->pGlyphList = EngAllocMem (0,
|
|
glyph_count * sizeof (GLYPH_LIST),
|
|
ALLOC_TAG);
|
|
if (NULL == ppdev->pGlyphList)
|
|
{
|
|
ppdev->ulGlyphListCount = 0;
|
|
return FALSE;
|
|
}
|
|
else
|
|
ppdev->ulGlyphListCount = glyph_count;
|
|
}
|
|
|
|
// Scan the string looking for glyphs to cache
|
|
|
|
glyph_count = 0;
|
|
if (enum_glyphs)
|
|
STROBJ_vEnumStart (pstro);
|
|
do
|
|
{
|
|
if (enum_glyphs)
|
|
more_glyphs = STROBJ_bEnum (pstro, &cGlyphs, &GlyphList);
|
|
|
|
for (i = 0; i < cGlyphs; i++)
|
|
{
|
|
pgb = GlyphList[i].pgdf->pgb;
|
|
glyph = &ppdev->pGlyphList[glyph_count];
|
|
|
|
prev_glyph = NULL;
|
|
hashed_index = HASH_HGLYPH (GlyphList[i].hg);
|
|
cur_glyph = cached_glyphs[hashed_index];
|
|
|
|
while (NULL != cur_glyph)
|
|
{
|
|
if (cur_glyph->hg >= GlyphList[i].hg)
|
|
break;
|
|
prev_glyph = cur_glyph;
|
|
cur_glyph = cur_glyph->next;
|
|
}
|
|
|
|
// Add a new glyph to the cache if we didn't get a match
|
|
|
|
if ((NULL == cur_glyph) || (cur_glyph->hg != GlyphList[i].hg))
|
|
{
|
|
new_glyph = store_glyph (cur_glyph, &GlyphList[i]);
|
|
if (NULL == new_glyph)
|
|
return FALSE;
|
|
if (NULL == prev_glyph)
|
|
cached_glyphs[hashed_index] = new_glyph;
|
|
else
|
|
prev_glyph->next = new_glyph;
|
|
glyph->cached_info = new_glyph;
|
|
}
|
|
else
|
|
glyph->cached_info = cur_glyph;
|
|
|
|
glyph->rect.left = GlyphList[i].ptl.x + pgb->ptlOrigin.x;
|
|
glyph->rect.top = GlyphList[i].ptl.y + pgb->ptlOrigin.y;
|
|
glyph->rect.right = glyph->rect.left + pgb->sizlBitmap.cx;
|
|
glyph->rect.bottom = glyph->rect.top + pgb->sizlBitmap.cy;
|
|
|
|
glyph_count++;
|
|
}
|
|
}
|
|
while (more_glyphs);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* fix_fixed_pitch_glyph_positions
|
|
*
|
|
* For reasons that I don't understand, Windows-NT makes a distinction between
|
|
* fixed pitch fonts and proportional fonts. Fixed pitch fonts don't get
|
|
* valid x/y locations for any other than the first glyph in the string. This
|
|
* routine scans the string and fills in the x/y location for the other glyphs.
|
|
*/
|
|
|
|
//static
|
|
void fix_fixed_pitch_glyph_positions (STROBJ *pstro)
|
|
{
|
|
BOOL more_glyphs; // Flags whether there are more glyph arrays to process
|
|
int cGlyphs; // Counter of glyphs in the current STROBJ
|
|
GLYPHPOS *GlyphList; // Array of glyphs for the current STROBJ
|
|
int iGlyph; // Glyph index in a STROBJ
|
|
LONG x, y;
|
|
|
|
// Scan through each of the strings and set the glyph positions
|
|
|
|
STROBJ_vEnumStart(pstro);
|
|
|
|
do
|
|
{
|
|
more_glyphs = STROBJ_bEnum (pstro, &cGlyphs, &GlyphList);
|
|
|
|
// Make sure we got a valid pointer back
|
|
// Make sure that cGlyphs != 0
|
|
|
|
if ((NULL != GlyphList) && (cGlyphs != 0))
|
|
{
|
|
x = GlyphList[0].ptl.x;
|
|
y = GlyphList[0].ptl.y;
|
|
|
|
for (iGlyph = 1; iGlyph < cGlyphs; iGlyph++)
|
|
{
|
|
x += pstro->ulCharInc;
|
|
GlyphList[iGlyph].ptl.x = x;
|
|
GlyphList[iGlyph].ptl.y = y;
|
|
}
|
|
}
|
|
} while (more_glyphs);
|
|
|
|
}
|
|
|
|
#ifndef TEST_ENV
|
|
/*
|
|
* dump_text
|
|
*
|
|
* This routine dumps information about a DrvTextOut call so that we can
|
|
* analyze what we did wrong or cases that we didn't handle
|
|
*/
|
|
|
|
//static
|
|
void dump_text (SURFOBJ *pso,
|
|
STROBJ *pstro,
|
|
FONTOBJ *pfo,
|
|
CLIPOBJ *pco,
|
|
RECTL *prclExtra,
|
|
RECTL *prclOpaque,
|
|
BRUSHOBJ *pboFore,
|
|
BRUSHOBJ *pboOpaque,
|
|
POINTL *pptlOrg,
|
|
MIX mix)
|
|
{
|
|
RECTL *rect_list;
|
|
|
|
// Dump the clip object
|
|
|
|
DISPDBG ((0, "Clip Object:\n"));
|
|
DumpCLIPOBJ (pco);
|
|
|
|
// Dump the list of "extra" rectangles
|
|
|
|
if (NULL == prclExtra)
|
|
DISPDBG ((0, "Extra Rectangles: None\n"));
|
|
else
|
|
{
|
|
DISPDBG ((0, "Extra Rectangles:\n"));
|
|
rect_list = prclExtra;
|
|
do
|
|
{
|
|
DISPDBG ((0, " "));
|
|
DumpRECTL (rect_list);
|
|
rect_list++;
|
|
} while ((rect_list->top != 0) || (rect_list->bottom != 0) ||
|
|
(rect_list->right != 0) || (rect_list->left != 0));
|
|
}
|
|
|
|
// Dump the opaque rectangle
|
|
|
|
DISPDBG ((0, "Opaque Rectangle: "));
|
|
DumpRECTL (prclOpaque);
|
|
|
|
// Dump the brushes
|
|
|
|
DISPDBG ((0, "Foreground Brush:\n"));
|
|
DumpBRUSHOBJ (pboFore);
|
|
DISPDBG ((0, "Opaque Brush:\n"));
|
|
DumpBRUSHOBJ (pboOpaque);
|
|
DISPDBG ((0, "Brush Origin: "));
|
|
DumpPOINTL (pptlOrg);
|
|
|
|
DISPDBG ((0, "Mix: 0x%08x\n", mix));
|
|
|
|
// Dump the string object
|
|
|
|
DumpSTROBJ (pstro);
|
|
}
|
|
#endif // TEST_ENV
|
|
|
|
/*
|
|
* punt_text
|
|
*
|
|
* This routine punts a DrvTextOut call back to GDI
|
|
*/
|
|
|
|
BOOL punt_text (SURFOBJ *pso,
|
|
STROBJ *pstro,
|
|
FONTOBJ *pfo,
|
|
CLIPOBJ *pco,
|
|
RECTL *prclExtra,
|
|
RECTL *prclOpaque,
|
|
BRUSHOBJ *pboFore,
|
|
BRUSHOBJ *pboOpaque,
|
|
POINTL *pptlOrg,
|
|
MIX mix)
|
|
{
|
|
#ifdef TEST_ENV
|
|
DISPDBG ((0, "Text punted\n"));
|
|
return FALSE;
|
|
#else
|
|
BOOL status;
|
|
BOOL old_bInPuntRoutine;
|
|
PPDEV ppdev = (PPDEV)pso->dhpdev;
|
|
|
|
DISPDBG ((1, "TGA.DLL!punt_text - Entry\n"));
|
|
|
|
BUMP_TGA_STAT(pStats->textpunts);
|
|
|
|
#ifdef DUMP_PUNTED_TEXT
|
|
dump_text (pso, pstro, pfo, pco, prclExtra, prclOpaque,
|
|
pboFore, pboOpaque, pptlOrg, mix);
|
|
#endif
|
|
|
|
// Force back to simple mode and wait for memory to flush
|
|
|
|
if (! ppdev->bSimpleMode)
|
|
vSetSimpleMode (ppdev);
|
|
|
|
// Punt the call
|
|
|
|
PUNT_GET_BITS (ppdev, CHOOSE_RECT (ppdev, pco));
|
|
|
|
old_bInPuntRoutine = ppdev->bInPuntRoutine;
|
|
ppdev->bInPuntRoutine = TRUE;
|
|
|
|
status = EngTextOut (ppdev->pPuntSurf, pstro, pfo, pco, prclExtra,
|
|
prclOpaque, pboFore, pboOpaque, pptlOrg, mix);
|
|
|
|
ppdev->bInPuntRoutine = old_bInPuntRoutine;
|
|
|
|
PUNT_PUT_BITS (status, ppdev, CHOOSE_RECT (ppdev, pco));
|
|
|
|
DISPDBG ((1, "TGA.DLL!punt_text - Exit %d\n", status));
|
|
|
|
return status;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* DrvTextOut
|
|
*
|
|
* This routine is called by GDI to write text to the screen
|
|
*/
|
|
|
|
BOOL DrvTextOut (SURFOBJ *pso, // Surface we're writing to
|
|
STROBJ *pstro, // List of strings to write
|
|
FONTOBJ *pfo, // Font we're using
|
|
CLIPOBJ *pco, // Clip list for this string
|
|
RECTL *prclExtra, // Extra rectangles to be displayed
|
|
RECTL *prclOpaque, // Opaque rectangle
|
|
BRUSHOBJ *pboFore, // Foreground brush (text bits)
|
|
BRUSHOBJ *pboOpaque, // Background brush
|
|
POINTL *pptlOrg, // Brush origin
|
|
MIX mix)
|
|
{
|
|
PPDEV ppdev;
|
|
ULONG mode;
|
|
ULONG rop;
|
|
ULONG color;
|
|
RECTL clip_rect;
|
|
BOOL more_rects;
|
|
|
|
DISPDBG ((1, "TGA.DLL!DrvTextOut - Entry\n"));
|
|
|
|
BUMP_TGA_STAT(pStats->text);
|
|
|
|
ppdev = (PPDEV) pso->dhpdev;
|
|
|
|
#if 0
|
|
dump_text (pso, pstro, pfo, pco, prclExtra, prclOpaque,
|
|
pboFore, pboOpaque, pptlOrg, mix);
|
|
#endif
|
|
|
|
WBFLUSH (ppdev);
|
|
|
|
// If this is a fixed pitch font, fill in the glyph offsets, since NT didn't
|
|
// do it for us
|
|
|
|
if (0 != pstro->ulCharInc)
|
|
fix_fixed_pitch_glyph_positions (pstro);
|
|
|
|
// If we've never seen this font before, allocate a glyph list. Then scan
|
|
// the string to make sure that we've got all of the glyphs in our cache
|
|
|
|
if (NULL == pfo->pvConsumer)
|
|
pfo->pvConsumer = EngAllocMem (FL_ZERO_MEMORY,
|
|
sizeof(CACHED_GLYPH_INFO *) * GLYPH_LIST_SIZE,
|
|
ALLOC_TAG);
|
|
if (NULL == pfo->pvConsumer)
|
|
{
|
|
DISPDBG ((0, "Failed to allocate glyph list!\n"));
|
|
return punt_text (pso, pstro, pfo, pco, prclExtra, prclOpaque,
|
|
pboFore, pboOpaque, pptlOrg, mix);
|
|
}
|
|
|
|
if (! cache_glyphs (ppdev, pstro, pfo->pvConsumer))
|
|
{
|
|
DISPDBG ((0, "Failed to cache glyphs!\n"));
|
|
return punt_text (pso, pstro, pfo, pco, prclExtra, prclOpaque,
|
|
pboFore, pboOpaque, pptlOrg, mix);
|
|
}
|
|
|
|
// Note that the TGA registers are no longer set for punting to GDI
|
|
|
|
ppdev->bSimpleMode = FALSE;
|
|
|
|
// Protect the driver from a potentially NULL clip object.
|
|
// ppdev->pcoTrivial is initialized to be a trivial clipobj.
|
|
|
|
if (NULL == pco)
|
|
pco = ppdev->pcoTrivial;
|
|
|
|
// If the opaque rectangle isn't NULL, fill the region
|
|
|
|
if (NULL != prclOpaque)
|
|
fill_opaque (pso, prclOpaque, pco, pboOpaque->iSolidColor);
|
|
|
|
// Set foreground color. Don't bother setting background color if we're
|
|
// in opaque stipple mode, since we won't use it
|
|
|
|
color = pboFore->iSolidColor;
|
|
if (BMF_8BPP == SURFOBJ_format (pso))
|
|
{
|
|
color |= color << 8;
|
|
color |= color << 16;
|
|
}
|
|
else
|
|
color &= 0x00ffffff;
|
|
|
|
TGAFOREGROUND (ppdev, color);
|
|
|
|
// Set the mode and Raster Op registers. Note that the CYCLE_REGS
|
|
// *SHOULDN'T* be necessary since I carefully ordered the TGA registers
|
|
// that are being set, but without it text doesn`t display properly.
|
|
// Leaving it in doesn't cost much. Take it out at your own risk...
|
|
|
|
CYCLE_REGS (ppdev);
|
|
|
|
mode = ppdev->ulModeTemplate | TGA_MODE_TRANSPARENT_STIPPLE;
|
|
TGAMODE (ppdev, mode);
|
|
|
|
rop = ppdev->ulRopTemplate | TGA_ROP_COPY;
|
|
TGAROP (ppdev, rop);
|
|
|
|
// Clip the text as necessary
|
|
|
|
switch (pco->iDComplexity)
|
|
{
|
|
case DC_TRIVIAL:
|
|
stipple_text_trivial (pso, pstro, pfo->pvConsumer);
|
|
break;
|
|
|
|
case DC_RECT:
|
|
if (bIntersectRects (&clip_rect, &pco->rclBounds,
|
|
&pstro->rclBkGround))
|
|
stipple_text (pso, pstro, &clip_rect, pfo->pvConsumer);
|
|
break;
|
|
|
|
case DC_COMPLEX:
|
|
CLIPOBJ_cEnumStart (pco, FALSE, CT_RECTANGLES, CD_ANY, 0);
|
|
do
|
|
{
|
|
ENUMRECTS cur_rect;
|
|
|
|
more_rects = CLIPOBJ_bEnum (pco, sizeof(cur_rect),
|
|
(ULONG *)&cur_rect);
|
|
if (cur_rect.c > 0)
|
|
{
|
|
if (bIntersectRects (&clip_rect, &cur_rect.arcl[0],
|
|
&pstro->rclBkGround))
|
|
stipple_text (pso, pstro, &clip_rect, pfo->pvConsumer);
|
|
}
|
|
} while (more_rects);
|
|
|
|
break;
|
|
}
|
|
|
|
// Check whether we have to leave TGA in simple mode
|
|
|
|
#ifndef TEST_ENV
|
|
if (ppdev->bInPuntRoutine)
|
|
vSetSimpleMode (ppdev);
|
|
#endif
|
|
|
|
DISPDBG ((1, "TGA.DLL!DrvTextOut - Exit\n"));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* DrvDestroyFont
|
|
*
|
|
* This routine is called byt GDI when a font is no longer needed
|
|
*/
|
|
|
|
VOID DrvDestroyFont (FONTOBJ *pfo)
|
|
{
|
|
CACHED_GLYPH_INFO **cached_glyphs;
|
|
int i;
|
|
CACHED_GLYPH_INFO *next_glyph, *cur_glyph;
|
|
|
|
DISPDBG ((1, "DrvDestroyFont - Entry\n"));
|
|
|
|
// If we haven't stored anything in the pvConsumer field, we haven't cached
|
|
// the font
|
|
|
|
cached_glyphs = pfo->pvConsumer;
|
|
if (NULL == cached_glyphs)
|
|
{
|
|
DISPDBG ((1, "DrvDestroyExit - Entry, pfo->pvConsumer == NULL\n"));
|
|
return;
|
|
}
|
|
|
|
// Zap the consumer field so we don't try to free this font again
|
|
|
|
pfo->pvConsumer = NULL;
|
|
|
|
// Walk the list of glyphs freeing any glyphs that we've cached
|
|
|
|
for (i = 0; i < GLYPH_LIST_SIZE; i++)
|
|
{
|
|
cur_glyph = cached_glyphs[i];
|
|
|
|
while (NULL != cur_glyph)
|
|
{
|
|
next_glyph = cur_glyph->next;
|
|
EngFreeMem (cur_glyph->bitmap);
|
|
EngFreeMem (cur_glyph);
|
|
cur_glyph = next_glyph;
|
|
}
|
|
}
|
|
|
|
// Free the glyph list
|
|
|
|
EngFreeMem (cached_glyphs);
|
|
|
|
DISPDBG ((1, "DrvDestroyExit - Exit\n"));
|
|
}
|