brush.c 9.36 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1
/*
2
 * X11DRV brush objects
Alexandre Julliard's avatar
Alexandre Julliard committed
3 4
 *
 * Copyright 1993, 1994  Alexandre Julliard
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
Alexandre Julliard's avatar
Alexandre Julliard committed
19 20
 */

Patrik Stridvall's avatar
Patrik Stridvall committed
21 22
#include "config.h"

Alexandre Julliard's avatar
Alexandre Julliard committed
23
#include <stdlib.h>
24

25
#include "wine/winbase16.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
26
#include "x11drv.h"
27
#include "wine/debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
28

29
WINE_DEFAULT_DEBUG_CHANNEL(gdi);
30

31
static const char HatchBrushes[][8] =
Alexandre Julliard's avatar
Alexandre Julliard committed
32 33 34 35 36 37
{
    { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 }, /* HS_HORIZONTAL */
    { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 }, /* HS_VERTICAL   */
    { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 }, /* HS_FDIAGONAL  */
    { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }, /* HS_BDIAGONAL  */
    { 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08, 0x08 }, /* HS_CROSS      */
Alexandre Julliard's avatar
Alexandre Julliard committed
38
    { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 }, /* HS_DIAGCROSS  */
Alexandre Julliard's avatar
Alexandre Julliard committed
39 40 41
};

  /* Levels of each primary for dithering */
42
#define PRIMARY_LEVELS  3
Alexandre Julliard's avatar
Alexandre Julliard committed
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
#define TOTAL_LEVELS    (PRIMARY_LEVELS*PRIMARY_LEVELS*PRIMARY_LEVELS)

 /* Dithering matrix size  */
#define MATRIX_SIZE     8
#define MATRIX_SIZE_2   (MATRIX_SIZE*MATRIX_SIZE)

  /* Total number of possible levels for a dithered primary color */
#define DITHER_LEVELS   (MATRIX_SIZE_2 * (PRIMARY_LEVELS-1) + 1)

  /* Dithering matrix */
static const int dither_matrix[MATRIX_SIZE_2] =
{
     0, 32,  8, 40,  2, 34, 10, 42,
    48, 16, 56, 24, 50, 18, 58, 26,
    12, 44,  4, 36, 14, 46,  6, 38,
    60, 28, 52, 20, 62, 30, 54, 22,
     3, 35, 11, 43,  1, 33,  9, 41,
    51, 19, 59, 27, 49, 17, 57, 25,
    15, 47,  7, 39, 13, 45,  5, 37,
    63, 31, 55, 23, 61, 29, 53, 21
};

  /* Mapping between (R,G,B) triples and EGA colors */
static const int EGAmapping[TOTAL_LEVELS] =
{
    0,  /* 000000 -> 000000 */
    4,  /* 00007f -> 000080 */
    12, /* 0000ff -> 0000ff */
    2,  /* 007f00 -> 008000 */
    6,  /* 007f7f -> 008080 */
    6,  /* 007fff -> 008080 */
    10, /* 00ff00 -> 00ff00 */
    6,  /* 00ff7f -> 008080 */
    14, /* 00ffff -> 00ffff */
    1,  /* 7f0000 -> 800000 */
    5,  /* 7f007f -> 800080 */
    5,  /* 7f00ff -> 800080 */
    3,  /* 7f7f00 -> 808000 */
    8,  /* 7f7f7f -> 808080 */
    7,  /* 7f7fff -> c0c0c0 */
    3,  /* 7fff00 -> 808000 */
    7,  /* 7fff7f -> c0c0c0 */
    7,  /* 7fffff -> c0c0c0 */
    9,  /* ff0000 -> ff0000 */
    5,  /* ff007f -> 800080 */
    13, /* ff00ff -> ff00ff */
    3,  /* ff7f00 -> 808000 */
    7,  /* ff7f7f -> c0c0c0 */
    7,  /* ff7fff -> c0c0c0 */
    11, /* ffff00 -> ffff00 */
    7,  /* ffff7f -> c0c0c0 */
    15  /* ffffff -> ffffff */
};

#define PIXEL_VALUE(r,g,b) \
98
    X11DRV_PALETTE_mapEGAPixel[EGAmapping[((r)*PRIMARY_LEVELS+(g))*PRIMARY_LEVELS+(b)]]
Alexandre Julliard's avatar
Alexandre Julliard committed
99

100 101
static const COLORREF BLACK = RGB(0, 0, 0);
static const COLORREF WHITE = RGB(0xff, 0xff, 0xff);
Alexandre Julliard's avatar
Alexandre Julliard committed
102 103 104 105

/***********************************************************************
 *           BRUSH_DitherColor
 */
106
static Pixmap BRUSH_DitherColor( COLORREF color, int depth)
Alexandre Julliard's avatar
Alexandre Julliard committed
107
{
108 109
    /* X image for building dithered pixmap */
    static XImage *ditherImage = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
110 111 112
    static COLORREF prevColor = 0xffffffff;
    unsigned int x, y;
    Pixmap pixmap;
113
    GC gc;
Alexandre Julliard's avatar
Alexandre Julliard committed
114

115
    XLockDisplay( gdi_display );
116 117
    if (!ditherImage)
    {
118
        ditherImage = XCreateImage( gdi_display, default_visual.visual, depth, ZPixmap, 0,
119 120
                                    NULL, MATRIX_SIZE, MATRIX_SIZE, 32, 0 );
        if (!ditherImage)
121 122
        {
            ERR("Could not create dither image\n");
123
            XUnlockDisplay( gdi_display );
124 125
            return 0;
        }
126 127
        ditherImage->data = HeapAlloc( GetProcessHeap(), 0,
                                       ditherImage->height * ditherImage->bytes_per_line );
128 129
    }

Alexandre Julliard's avatar
Alexandre Julliard committed
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
    if (color != prevColor)
    {
	int r = GetRValue( color ) * DITHER_LEVELS;
	int g = GetGValue( color ) * DITHER_LEVELS;
	int b = GetBValue( color ) * DITHER_LEVELS;
	const int *pmatrix = dither_matrix;

	for (y = 0; y < MATRIX_SIZE; y++)
	{
	    for (x = 0; x < MATRIX_SIZE; x++)
	    {
		int d  = *pmatrix++ * 256;
		int dr = ((r + d) / MATRIX_SIZE_2) / 256;
		int dg = ((g + d) / MATRIX_SIZE_2) / 256;
		int db = ((b + d) / MATRIX_SIZE_2) / 256;
Alexandre Julliard's avatar
Alexandre Julliard committed
145
		XPutPixel( ditherImage, x, y, PIXEL_VALUE(dr,dg,db) );
Alexandre Julliard's avatar
Alexandre Julliard committed
146 147 148 149
	    }
	}
	prevColor = color;
    }
150

151
    pixmap = XCreatePixmap( gdi_display, root_window, MATRIX_SIZE, MATRIX_SIZE, depth );
152 153 154
    gc = XCreateGC( gdi_display, pixmap, 0, NULL );
    XPutImage( gdi_display, pixmap, gc, ditherImage, 0, 0, 0, 0, MATRIX_SIZE, MATRIX_SIZE );
    XFreeGC( gdi_display, gc );
155
    XUnlockDisplay( gdi_display );
156

Alexandre Julliard's avatar
Alexandre Julliard committed
157 158 159 160
    return pixmap;
}


161 162 163 164 165 166 167 168 169 170 171 172 173 174
/***********************************************************************
 *           BRUSH_DitherMono
 */
static Pixmap BRUSH_DitherMono( COLORREF color )
{
    /* This makes the spray work in Win 3.11 pbrush.exe */
    /* FIXME. Extend this basic selection of dither patterns */
    static const char gray_dither[][2] = {{ 0x1, 0x0 }, /* DKGRAY */
                                          { 0x2, 0x1 }, /* GRAY */
                                          { 0x1, 0x3 }, /* LTGRAY */
    };                                      
    int gray = (30 * GetRValue(color) + 59 * GetGValue(color) + 11 * GetBValue(color)) / 100;
    int idx = gray * (sizeof gray_dither/sizeof gray_dither[0] + 1)/256 - 1;

175
    TRACE("color=%06x -> gray=%x\n", color, gray);
176
    return XCreateBitmapFromData( gdi_display, root_window, gray_dither[idx], 2, 2 );
177 178
}

Alexandre Julliard's avatar
Alexandre Julliard committed
179 180 181
/***********************************************************************
 *           BRUSH_SelectSolidBrush
 */
182
static void BRUSH_SelectSolidBrush( X11DRV_PDEVICE *physDev, COLORREF color )
Alexandre Julliard's avatar
Alexandre Julliard committed
183
{
184
    COLORREF colorRGB = X11DRV_PALETTE_GetColor( physDev, color );
185
    if ((physDev->depth > 1) && (default_visual.depth <= 8) && !X11DRV_IsSolidColor( color ))
Alexandre Julliard's avatar
Alexandre Julliard committed
186 187
    {
	  /* Dithered brush */
188
	physDev->brush.pixmap = BRUSH_DitherColor( colorRGB, physDev->depth );
189 190
	physDev->brush.fillStyle = FillTiled;
	physDev->brush.pixel = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
191
    }
192
    else if (physDev->depth == 1 && colorRGB != WHITE && colorRGB != BLACK)
193 194
    {
	physDev->brush.pixel = 0;
195
	physDev->brush.pixmap = BRUSH_DitherMono( colorRGB );
196 197
	physDev->brush.fillStyle = FillTiled;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
198 199 200
    else
    {
	  /* Solid brush */
201
	physDev->brush.pixel = X11DRV_PALETTE_ToPhysical( physDev, color );
202
	physDev->brush.fillStyle = FillSolid;
Alexandre Julliard's avatar
Alexandre Julliard committed
203 204 205 206
    }
}


207 208
static BOOL select_pattern_brush( X11DRV_PDEVICE *physdev, const struct brush_pattern *pattern )
{
209
    XVisualInfo vis = default_visual;
210 211 212
    Pixmap pixmap;
    const BITMAPINFO *info = pattern->info;

213
    if (physdev->depth == 1 || info->bmiHeader.biBitCount == 1) vis.depth = 1;
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232

    pixmap = create_pixmap_from_image( physdev->dev.hdc, &vis, info, &pattern->bits, pattern->usage );
    if (!pixmap) return FALSE;

    if (physdev->brush.pixmap) XFreePixmap( gdi_display, physdev->brush.pixmap );
    physdev->brush.pixmap = pixmap;

    if (vis.depth == 1)
    {
	physdev->brush.fillStyle = FillOpaqueStippled;
	physdev->brush.pixel = -1;  /* Special case (see DC_SetupGCForBrush) */
    }
    else
    {
	physdev->brush.fillStyle = FillTiled;
	physdev->brush.pixel = 0;  /* Ignored */
    }
    return TRUE;
}
233

Alexandre Julliard's avatar
Alexandre Julliard committed
234
/***********************************************************************
235
 *           SelectBrush   (X11DRV.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
236
 */
237
HBRUSH X11DRV_SelectBrush( PHYSDEV dev, HBRUSH hbrush, const struct brush_pattern *pattern )
Alexandre Julliard's avatar
Alexandre Julliard committed
238
{
239
    X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );
240 241
    LOGBRUSH logbrush;

242
    if (pattern)  /* pattern brush */
243
    {
244
        if (!select_pattern_brush( physDev, pattern )) return 0;
245 246 247 248 249
        TRACE("BS_PATTERN\n");
        physDev->brush.style = BS_PATTERN;
        return hbrush;
    }

250 251
    if (!GetObjectA( hbrush, sizeof(logbrush), &logbrush )) return 0;

252
    TRACE("hdc=%p hbrush=%p\n", dev->hdc, hbrush);
Alexandre Julliard's avatar
Alexandre Julliard committed
253

254
    if (physDev->brush.pixmap)
Alexandre Julliard's avatar
Alexandre Julliard committed
255
    {
256 257
        XFreePixmap( gdi_display, physDev->brush.pixmap );
        physDev->brush.pixmap = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
258
    }
259
    physDev->brush.style = logbrush.lbStyle;
260
    if (hbrush == GetStockObject( DC_BRUSH ))
261
        logbrush.lbColor = GetDCBrushColor( dev->hdc );
262

263
    switch(logbrush.lbStyle)
Alexandre Julliard's avatar
Alexandre Julliard committed
264 265
    {
      case BS_NULL:
266
	TRACE("BS_NULL\n" );
Alexandre Julliard's avatar
Alexandre Julliard committed
267 268 269
	break;

      case BS_SOLID:
270
        TRACE("BS_SOLID\n" );
271
	BRUSH_SelectSolidBrush( physDev, logbrush.lbColor );
Alexandre Julliard's avatar
Alexandre Julliard committed
272
	break;
273

Alexandre Julliard's avatar
Alexandre Julliard committed
274
      case BS_HATCHED:
275
	TRACE("BS_HATCHED\n" );
276
	physDev->brush.pixel = X11DRV_PALETTE_ToPhysical( physDev, logbrush.lbColor );
277 278
        physDev->brush.pixmap = XCreateBitmapFromData( gdi_display, root_window,
                                                       HatchBrushes[logbrush.lbHatch], 8, 8 );
279
	physDev->brush.fillStyle = FillStippled;
Alexandre Julliard's avatar
Alexandre Julliard committed
280 281
	break;
    }
282
    return hbrush;
Alexandre Julliard's avatar
Alexandre Julliard committed
283
}
284 285 286 287 288


/***********************************************************************
 *           SetDCBrushColor (X11DRV.@)
 */
289
COLORREF X11DRV_SetDCBrushColor( PHYSDEV dev, COLORREF crColor )
290
{
291 292
    X11DRV_PDEVICE *physDev = get_x11drv_dev( dev );

293
    if (GetCurrentObject(dev->hdc, OBJ_BRUSH) == GetStockObject( DC_BRUSH ))
294 295 296 297
        BRUSH_SelectSolidBrush( physDev, crColor );

    return crColor;
}