/*
 * GDI brush objects
 *
 * Copyright 1993, 1994  Alexandre Julliard
 *
 * 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
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "config.h"

#include <stdarg.h>
#include <string.h>

#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "gdi_private.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(gdi);

/* GDI logical brush object */
typedef struct
{
    GDIOBJHDR             header;
    LOGBRUSH              logbrush;
    HBITMAP               bitmap;   /* bitmap handle for DDB pattern brushes */
    BITMAPINFO           *info;     /* DIB info for pattern brushes */
    struct gdi_image_bits bits;     /* DIB bits for pattern brushes */
    UINT                  usage;    /* color usage for DIB info */
} BRUSHOBJ;

#define NB_HATCH_STYLES  6

static HGDIOBJ BRUSH_SelectObject( HGDIOBJ handle, HDC hdc );
static INT BRUSH_GetObject( HGDIOBJ handle, INT count, LPVOID buffer );
static BOOL BRUSH_DeleteObject( HGDIOBJ handle );

static const struct gdi_obj_funcs brush_funcs =
{
    BRUSH_SelectObject,  /* pSelectObject */
    BRUSH_GetObject,     /* pGetObjectA */
    BRUSH_GetObject,     /* pGetObjectW */
    NULL,                /* pUnrealizeObject */
    BRUSH_DeleteObject   /* pDeleteObject */
};


/* fetch the contents of the brush bitmap and cache them in the brush object */
static BOOL store_bitmap_bits( BRUSHOBJ *brush, BITMAPOBJ *bmp )
{
    const struct gdi_dc_funcs *funcs = get_bitmap_funcs( bmp );
    struct gdi_image_bits bits;
    struct bitblt_coords src;
    BITMAPINFO *info;

    if (!(info = HeapAlloc( GetProcessHeap(), 0, FIELD_OFFSET( BITMAPINFO, bmiColors[256] ))))
        return FALSE;

    src.visrect.left   = src.x = 0;
    src.visrect.top    = src.y = 0;
    src.visrect.right  = src.width = bmp->bitmap.bmWidth;
    src.visrect.bottom = src.height = bmp->bitmap.bmHeight;
    if (funcs->pGetImage( NULL, brush->bitmap, info, &bits, &src ))
    {
        HeapFree( GetProcessHeap(), 0, info );
        return FALSE;
    }

    if (info->bmiHeader.biBitCount <= 8 && !info->bmiHeader.biClrUsed) fill_default_color_table( info );

    /* release the unneeded space */
    HeapReAlloc( GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY, info,
                 bitmap_info_size( info, DIB_RGB_COLORS ));
    brush->info  = info;
    brush->bits  = bits;
    brush->usage = DIB_RGB_COLORS;
    return TRUE;
}

static BOOL copy_bitmap( BRUSHOBJ *brush, HBITMAP bitmap )
{
    BITMAPINFO *info;
    BITMAPOBJ *bmp = GDI_GetObjPtr( bitmap, OBJ_BITMAP );

    if (!bmp) return FALSE;

    if (!bmp->dib)
    {
        if ((brush->bitmap = CreateBitmap( bmp->bitmap.bmWidth, bmp->bitmap.bmHeight,
                                           bmp->bitmap.bmPlanes, bmp->bitmap.bmBitsPixel, NULL )))
        {
            if (bmp->funcs->pCopyBitmap( bitmap, brush->bitmap ))
            {
                BITMAPOBJ *copy = GDI_GetObjPtr( brush->bitmap, OBJ_BITMAP );
                copy->funcs = bmp->funcs;
                GDI_ReleaseObj( copy );
            }
            else
            {
                DeleteObject( brush->bitmap );
                brush->bitmap = 0;
            }
        }
        GDI_ReleaseObj( bitmap );
        return brush->bitmap != 0;
    }

    info = HeapAlloc( GetProcessHeap(), 0,
                      bitmap_info_size( (BITMAPINFO *)&bmp->dib->dsBmih, DIB_RGB_COLORS ));
    if (!info) goto done;
    info->bmiHeader = bmp->dib->dsBmih;
    if (bmp->dib->dsBmih.biCompression == BI_BITFIELDS)
        memcpy( &info->bmiHeader + 1, bmp->dib->dsBitfields, sizeof(bmp->dib->dsBitfields) );
    else if (bmp->nb_colors)
        memcpy( &info->bmiHeader + 1, bmp->color_table, bmp->nb_colors * sizeof(RGBQUAD) );
    if (!(brush->bits.ptr = HeapAlloc( GetProcessHeap(), 0, get_dib_image_size( info ))))
    {
        HeapFree( GetProcessHeap(), 0, info );
        goto done;
    }
    memcpy( brush->bits.ptr, bmp->dib->dsBm.bmBits, get_dib_image_size( info ));
    brush->bits.is_copy = TRUE;
    brush->bits.free = free_heap_bits;
    brush->info = info;
    brush->usage = DIB_RGB_COLORS;

done:
    GDI_ReleaseObj( bitmap );
    return brush->info != NULL;
}

BOOL get_brush_bitmap_info( HBRUSH handle, BITMAPINFO *info, void **bits, UINT *usage )
{
    BRUSHOBJ *brush;
    BOOL ret = FALSE;

    if (!(brush = GDI_GetObjPtr( handle, OBJ_BRUSH ))) return FALSE;

    if (!brush->info)
    {
        BITMAPOBJ *bmp = GDI_GetObjPtr( brush->bitmap, OBJ_BITMAP );

        if (bmp)
        {
            store_bitmap_bits( brush, bmp );
            GDI_ReleaseObj( brush->bitmap );
        }
    }
    if (brush->info)
    {
        memcpy( info, brush->info, bitmap_info_size( brush->info, brush->usage ));
        *bits = brush->bits.ptr;
        *usage = brush->usage;
        ret = TRUE;
    }
    GDI_ReleaseObj( handle );
    return ret;
}


/***********************************************************************
 *           CreateBrushIndirect    (GDI32.@)
 *
 * Create a logical brush with a given style, color or pattern.
 *
 * PARAMS
 *  brush [I] Pointer to a LOGBRUSH structure describing the desired brush.
 *
 * RETURNS
 *  A handle to the created brush, or a NULL handle if the brush cannot be 
 *  created.
 *
 * NOTES
 * - The brush returned should be freed by the caller using DeleteObject()
 *   when it is no longer required.
 * - Windows 95 and earlier cannot create brushes from bitmaps or DIBs larger
 *   than 8x8 pixels. If a larger bitmap is given, only a portion of the bitmap
 *   is used.
 */
HBRUSH WINAPI CreateBrushIndirect( const LOGBRUSH * brush )
{
    BRUSHOBJ * ptr;
    HBRUSH hbrush;
    HGLOBAL hmem = 0;

    if (!(ptr = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ptr) ))) return 0;

    ptr->logbrush = *brush;

    switch (ptr->logbrush.lbStyle)
    {
    case BS_SOLID:
    case BS_HOLLOW:
    case BS_HATCHED:
        break;

    case BS_PATTERN8X8:
        ptr->logbrush.lbStyle = BS_PATTERN;
        /* fall through */
    case BS_PATTERN:
        if (!copy_bitmap( ptr, (HBITMAP)ptr->logbrush.lbHatch )) goto error;
        ptr->logbrush.lbColor = 0;
        break;

    case BS_DIBPATTERN:
        hmem = (HGLOBAL)ptr->logbrush.lbHatch;
        if (!(ptr->logbrush.lbHatch = (ULONG_PTR)GlobalLock( hmem ))) goto error;
        /* fall through */
    case BS_DIBPATTERNPT:
        ptr->usage = ptr->logbrush.lbColor;
        ptr->info = copy_packed_dib( (BITMAPINFO *)ptr->logbrush.lbHatch, ptr->usage );
        if (hmem) GlobalUnlock( hmem );
        if (!ptr->info) goto error;
        ptr->bits.ptr = (char *)ptr->info + bitmap_info_size( ptr->info, ptr->usage );
        ptr->logbrush.lbStyle = BS_DIBPATTERN;
        ptr->logbrush.lbColor = 0;
        break;

    case BS_DIBPATTERN8X8:
    case BS_MONOPATTERN:
    case BS_INDEXED:
    default:
        WARN( "invalid brush style %u\n", ptr->logbrush.lbStyle );
        goto error;
    }

    if ((hbrush = alloc_gdi_handle( &ptr->header, OBJ_BRUSH, &brush_funcs )))
    {
        TRACE("%p\n", hbrush);
        return hbrush;
    }

 error:
    if (ptr->bitmap) DeleteObject( ptr->bitmap );
    HeapFree( GetProcessHeap(), 0, ptr->info );
    HeapFree( GetProcessHeap(), 0, ptr );
    return 0;
}


/***********************************************************************
 *           CreateHatchBrush    (GDI32.@)
 *
 * Create a logical brush with a hatched pattern.
 *
 * PARAMS
 *  style [I] Direction of lines for the hatch pattern (HS_* values from "wingdi.h")
 *  color [I] Colour of the hatched pattern
 *
 * RETURNS
 *  A handle to the created brush, or a NULL handle if the brush cannot
 *  be created.
 *
 * NOTES
 * - This function uses CreateBrushIndirect() to create the brush.
 * - The brush returned should be freed by the caller using DeleteObject()
 *   when it is no longer required.
 */
HBRUSH WINAPI CreateHatchBrush( INT style, COLORREF color )
{
    LOGBRUSH logbrush;

    TRACE("%d %06x\n", style, color );

    logbrush.lbStyle = BS_HATCHED;
    logbrush.lbColor = color;
    logbrush.lbHatch = style;

    return CreateBrushIndirect( &logbrush );
}


/***********************************************************************
 *           CreatePatternBrush    (GDI32.@)
 *
 * Create a logical brush with a pattern from a bitmap.
 *
 * PARAMS
 *  hbitmap  [I] Bitmap containing pattern for the brush
 *
 * RETURNS
 *  A handle to the created brush, or a NULL handle if the brush cannot 
 *  be created.
 *
 * NOTES
 * - This function uses CreateBrushIndirect() to create the brush.
 * - The brush returned should be freed by the caller using DeleteObject()
 *   when it is no longer required.
 */
HBRUSH WINAPI CreatePatternBrush( HBITMAP hbitmap )
{
    LOGBRUSH logbrush = { BS_PATTERN, 0, 0 };
    TRACE("%p\n", hbitmap );

    logbrush.lbHatch = (ULONG_PTR)hbitmap;
    return CreateBrushIndirect( &logbrush );
}


/***********************************************************************
 *           CreateDIBPatternBrush    (GDI32.@)
 *
 * Create a logical brush with a pattern from a DIB.
 *
 * PARAMS
 *  hbitmap  [I] Global object containing BITMAPINFO structure for the pattern
 *  coloruse [I] Specifies color format, if provided
 *
 * RETURNS
 *  A handle to the created brush, or a NULL handle if the brush cannot 
 *  be created.
 *
 * NOTES
 * - This function uses CreateBrushIndirect() to create the brush.
 * - The brush returned should be freed by the caller using DeleteObject()
 *   when it is no longer required.
 * - This function is for compatibility only. CreateDIBPatternBrushPt() should 
 *   be used instead.
 */
HBRUSH WINAPI CreateDIBPatternBrush( HGLOBAL hbitmap, UINT coloruse )
{
    LOGBRUSH logbrush;

    TRACE("%p\n", hbitmap );

    logbrush.lbStyle = BS_DIBPATTERN;
    logbrush.lbColor = coloruse;

    logbrush.lbHatch = (ULONG_PTR)hbitmap;

    return CreateBrushIndirect( &logbrush );
}


/***********************************************************************
 *           CreateDIBPatternBrushPt    (GDI32.@)
 *
 * Create a logical brush with a pattern from a DIB.
 *
 * PARAMS
 *  data     [I] Pointer to a BITMAPINFO structure and image data  for the pattern
 *  coloruse [I] Specifies color format, if provided
 *
 * RETURNS
 *  A handle to the created brush, or a NULL handle if the brush cannot
 *  be created.
 *
 * NOTES
 * - This function uses CreateBrushIndirect() to create the brush.
 * - The brush returned should be freed by the caller using DeleteObject()
 *   when it is no longer required.
 */
HBRUSH WINAPI CreateDIBPatternBrushPt( const void* data, UINT coloruse )
{
    const BITMAPINFO *info=data;
    LOGBRUSH logbrush;

    if (!data)
        return NULL;

    TRACE("%p %dx%d %dbpp\n", info, info->bmiHeader.biWidth,
	  info->bmiHeader.biHeight,  info->bmiHeader.biBitCount);

    logbrush.lbStyle = BS_DIBPATTERNPT;
    logbrush.lbColor = coloruse;
    logbrush.lbHatch = (ULONG_PTR)data;

    return CreateBrushIndirect( &logbrush );
}


/***********************************************************************
 *           CreateSolidBrush    (GDI32.@)
 *
 * Create a logical brush consisting of a single colour.
 *
 * PARAMS
 *  color [I] Colour to make the solid brush
 *
 * RETURNS
 *  A handle to the newly created brush, or a NULL handle if the brush cannot
 *  be created.
 *
 * NOTES
 * - This function uses CreateBrushIndirect() to create the brush.
 * - The brush returned should be freed by the caller using DeleteObject()
 *   when it is no longer required.
 */
HBRUSH WINAPI CreateSolidBrush( COLORREF color )
{
    LOGBRUSH logbrush;

    TRACE("%06x\n", color );

    logbrush.lbStyle = BS_SOLID;
    logbrush.lbColor = color;
    logbrush.lbHatch = 0;

    return CreateBrushIndirect( &logbrush );
}


/***********************************************************************
 *           SetBrushOrgEx    (GDI32.@)
 *
 * Set the brush origin for a device context.
 *
 * PARAMS
 *  hdc    [I] Device context to set the brush origin for
 *  x      [I] New x origin
 *  y      [I] New y origin
 *  oldorg [O] If non NULL, destination for previously set brush origin.
 *
 * RETURNS
 *  Success: TRUE. The origin is set to (x,y), and oldorg is updated if given.
 */
BOOL WINAPI SetBrushOrgEx( HDC hdc, INT x, INT y, LPPOINT oldorg )
{
    DC *dc = get_dc_ptr( hdc );

    if (!dc) return FALSE;
    if (oldorg)
    {
        oldorg->x = dc->brushOrgX;
        oldorg->y = dc->brushOrgY;
    }
    dc->brushOrgX = x;
    dc->brushOrgY = y;
    release_dc_ptr( dc );
    return TRUE;
}

/***********************************************************************
 *           FixBrushOrgEx    (GDI32.@)
 *
 * See SetBrushOrgEx.
 *
 * NOTES
 *  This function is no longer documented by MSDN, but in Win95 GDI32 it
 *  is the same as SetBrushOrgEx().
 */
BOOL WINAPI FixBrushOrgEx( HDC hdc, INT x, INT y, LPPOINT oldorg )
{
    return SetBrushOrgEx(hdc,x,y,oldorg);
}


/***********************************************************************
 *           BRUSH_SelectObject
 */
static HGDIOBJ BRUSH_SelectObject( HGDIOBJ handle, HDC hdc )
{
    BRUSHOBJ *brush;
    HGDIOBJ ret = 0;
    DC *dc = get_dc_ptr( hdc );

    if (!dc)
    {
        SetLastError( ERROR_INVALID_HANDLE );
        return 0;
    }

    if ((brush = GDI_GetObjPtr( handle, OBJ_BRUSH )))
    {
        PHYSDEV physdev = GET_DC_PHYSDEV( dc, pSelectBrush );
        HBITMAP bitmap = brush->bitmap;
        BITMAPINFO *info;
        void *bits;
        UINT usage;

        if (bitmap && !brush->info)
        {
            BITMAPOBJ *bmp = GDI_GetObjPtr( bitmap, OBJ_BITMAP );
            /* fetch the bitmap bits if we are selecting into a different type of DC */
            if (bmp && bmp->funcs != physdev->funcs) store_bitmap_bits( brush, bmp );
            GDI_ReleaseObj( bitmap );
        }

        info   = brush->info;
        bits   = brush->bits.ptr;
        usage  = brush->usage;
        GDI_inc_ref_count( handle );
        GDI_ReleaseObj( handle );

        if (!physdev->funcs->pSelectBrush( physdev, handle, bitmap, info, bits, usage ))
        {
            GDI_dec_ref_count( handle );
        }
        else
        {
            ret = dc->hBrush;
            dc->hBrush = handle;
            GDI_dec_ref_count( ret );
        }
    }
    release_dc_ptr( dc );
    return ret;
}


/***********************************************************************
 *           BRUSH_DeleteObject
 */
static BOOL BRUSH_DeleteObject( HGDIOBJ handle )
{
    BRUSHOBJ *brush = free_gdi_handle( handle );

    if (!brush) return FALSE;
    if (brush->bits.free) brush->bits.free( &brush->bits );
    if (brush->bitmap) DeleteObject( brush->bitmap );
    HeapFree( GetProcessHeap(), 0, brush->info );
    return HeapFree( GetProcessHeap(), 0, brush );
}


/***********************************************************************
 *           BRUSH_GetObject
 */
static INT BRUSH_GetObject( HGDIOBJ handle, INT count, LPVOID buffer )
{
    BRUSHOBJ *brush = GDI_GetObjPtr( handle, OBJ_BRUSH );

    if (!brush) return 0;
    if (buffer)
    {
        if (count > sizeof(brush->logbrush)) count = sizeof(brush->logbrush);
        memcpy( buffer, &brush->logbrush, count );
    }
    else count = sizeof(brush->logbrush);
    GDI_ReleaseObj( handle );
    return count;
}
