Back to index

lightning-sunbird  0.9+nobinonly
cairo-win32-surface.c
Go to the documentation of this file.
00001 /* Cairo - a vector graphics library with display and print output
00002  *
00003  * Copyright © 2005 Red Hat, Inc.
00004  *
00005  * This library is free software; you can redistribute it and/or
00006  * modify it either under the terms of the GNU Lesser General Public
00007  * License version 2.1 as published by the Free Software Foundation
00008  * (the "LGPL") or, at your option, under the terms of the Mozilla
00009  * Public License Version 1.1 (the "MPL"). If you do not alter this
00010  * notice, a recipient may use your version of this file under either
00011  * the MPL or the LGPL.
00012  *
00013  * You should have received a copy of the LGPL along with this library
00014  * in the file COPYING-LGPL-2.1; if not, write to the Free Software
00015  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00016  * You should have received a copy of the MPL along with this library
00017  * in the file COPYING-MPL-1.1
00018  *
00019  * The contents of this file are subject to the Mozilla Public License
00020  * Version 1.1 (the "License"); you may not use this file except in
00021  * compliance with the License. You may obtain a copy of the License at
00022  * http://www.mozilla.org/MPL/
00023  *
00024  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
00025  * OF ANY KIND, either express or implied. See the LGPL or the MPL for
00026  * the specific language governing rights and limitations.
00027  *
00028  * The Original Code is the cairo graphics library.
00029  *
00030  * The Initial Developer of the Original Code is Red Hat, Inc.
00031  *
00032  * Contributor(s):
00033  *     Owen Taylor <otaylor@redhat.com>
00034  */
00035 
00036 #include <stdio.h>
00037 #include "cairoint.h"
00038 #include "cairo-win32-private.h"
00039 
00040 static const cairo_surface_backend_t cairo_win32_surface_backend;
00041 
00051 cairo_status_t
00052 _cairo_win32_print_gdi_error (const char *context)
00053 {
00054     void *lpMsgBuf;
00055     DWORD last_error = GetLastError ();
00056 
00057     if (!FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER | 
00058                       FORMAT_MESSAGE_FROM_SYSTEM,
00059                       NULL,
00060                       last_error,
00061                       MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
00062                       (LPTSTR) &lpMsgBuf,
00063                       0, NULL)) {
00064        fprintf (stderr, "%s: Unknown GDI error", context);
00065     } else {
00066        fprintf (stderr, "%s: %s", context, (char *)lpMsgBuf);
00067        
00068        LocalFree (lpMsgBuf);
00069     }
00070 
00071     /* We should switch off of last_status, but we'd either return
00072      * CAIRO_STATUS_NO_MEMORY or CAIRO_STATUS_UNKNOWN_ERROR and there
00073      * is no CAIRO_STATUS_UNKNOWN_ERROR.
00074      */
00075 
00076     return CAIRO_STATUS_NO_MEMORY;
00077 }
00078 
00079 static cairo_status_t
00080 _create_dc_and_bitmap (cairo_win32_surface_t *surface,
00081                      HDC                    original_dc,
00082                      cairo_format_t         format,
00083                      int                    width,
00084                      int                    height,
00085                      char                 **bits_out,
00086                      int                   *rowstride_out)
00087 {
00088     cairo_status_t status;
00089 
00090     BITMAPINFO *bitmap_info = NULL;
00091     struct {
00092        BITMAPINFOHEADER bmiHeader;
00093        RGBQUAD bmiColors[2];
00094     } bmi_stack;
00095     void *bits;
00096 
00097     int num_palette = 0;    /* Quiet GCC */
00098     int i;
00099 
00100     surface->dc = NULL;
00101     surface->bitmap = NULL;
00102 
00103     switch (format) {
00104     case CAIRO_FORMAT_ARGB32:
00105     case CAIRO_FORMAT_RGB24:
00106        num_palette = 0;
00107        break;
00108        
00109     case CAIRO_FORMAT_A8:
00110        num_palette = 256;
00111        break;
00112        
00113     case CAIRO_FORMAT_A1:
00114        num_palette = 2;
00115        break;
00116     }
00117 
00118     if (num_palette > 2) {
00119        bitmap_info = malloc (sizeof (BITMAPINFOHEADER) + num_palette * sizeof (RGBQUAD));
00120        if (!bitmap_info)
00121            return CAIRO_STATUS_NO_MEMORY;
00122     } else {
00123        bitmap_info = (BITMAPINFO *)&bmi_stack;
00124     }
00125 
00126     bitmap_info->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
00127     bitmap_info->bmiHeader.biWidth = width == 0 ? 1 : width;
00128     bitmap_info->bmiHeader.biHeight = height == 0 ? -1 : - height; /* top-down */
00129     bitmap_info->bmiHeader.biSizeImage = 0;
00130     bitmap_info->bmiHeader.biXPelsPerMeter = 72. / 0.0254; /* unused here */
00131     bitmap_info->bmiHeader.biYPelsPerMeter = 72. / 0.0254; /* unused here */
00132     bitmap_info->bmiHeader.biPlanes = 1;
00133     
00134     switch (format) {
00135     case CAIRO_FORMAT_ARGB32:
00136     case CAIRO_FORMAT_RGB24:
00137        bitmap_info->bmiHeader.biBitCount = 32;
00138        bitmap_info->bmiHeader.biCompression = BI_RGB;
00139        bitmap_info->bmiHeader.biClrUsed = 0;     /* unused */
00140        bitmap_info->bmiHeader.biClrImportant = 0;
00141        break;
00142        
00143     case CAIRO_FORMAT_A8:
00144        bitmap_info->bmiHeader.biBitCount = 8;
00145        bitmap_info->bmiHeader.biCompression = BI_RGB;
00146        bitmap_info->bmiHeader.biClrUsed = 256;
00147        bitmap_info->bmiHeader.biClrImportant = 0;
00148 
00149        for (i = 0; i < 256; i++) {
00150            bitmap_info->bmiColors[i].rgbBlue = i;
00151            bitmap_info->bmiColors[i].rgbGreen = i;
00152            bitmap_info->bmiColors[i].rgbRed = i;
00153            bitmap_info->bmiColors[i].rgbReserved = 0;
00154        }
00155        
00156        break;
00157        
00158     case CAIRO_FORMAT_A1:
00159        bitmap_info->bmiHeader.biBitCount = 1;
00160        bitmap_info->bmiHeader.biCompression = BI_RGB;
00161        bitmap_info->bmiHeader.biClrUsed = 2;
00162        bitmap_info->bmiHeader.biClrImportant = 0;
00163 
00164        for (i = 0; i < 2; i++) {
00165            bitmap_info->bmiColors[i].rgbBlue = i * 255;
00166            bitmap_info->bmiColors[i].rgbGreen = i * 255;
00167            bitmap_info->bmiColors[i].rgbRed = i * 255;
00168            bitmap_info->bmiColors[i].rgbReserved = 0;
00169            break;
00170        }
00171     }
00172 
00173     surface->dc = CreateCompatibleDC (original_dc);
00174     if (!surface->dc)
00175        goto FAIL;
00176 
00177     surface->bitmap = CreateDIBSection (surface->dc,
00178                                      bitmap_info,
00179                                      DIB_RGB_COLORS,
00180                                      &bits,
00181                                      NULL, 0);
00182     if (!surface->bitmap)
00183        goto FAIL;
00184 
00185     surface->saved_dc_bitmap = SelectObject (surface->dc,
00186                                         surface->bitmap);
00187     if (!surface->saved_dc_bitmap)
00188        goto FAIL;
00189     
00190     if (bitmap_info && num_palette > 2)
00191        free (bitmap_info);
00192 
00193     if (bits_out)
00194        *bits_out = bits;
00195 
00196     if (rowstride_out) {
00197        /* Windows bitmaps are padded to 32-bit (dword) boundaries */
00198        switch (format) {
00199        case CAIRO_FORMAT_ARGB32:
00200        case CAIRO_FORMAT_RGB24:
00201            *rowstride_out = 4 * width;
00202            break;
00203            
00204        case CAIRO_FORMAT_A8:
00205            *rowstride_out = (width + 3) & ~3;
00206            break;
00207        
00208        case CAIRO_FORMAT_A1:
00209            *rowstride_out = ((width + 31) & ~31) / 8;
00210            break;
00211        }
00212     }
00213 
00214     return CAIRO_STATUS_SUCCESS;
00215 
00216  FAIL:
00217     status = _cairo_win32_print_gdi_error ("_create_dc_and_bitmap");
00218     
00219     if (bitmap_info && num_palette > 2)
00220        free (bitmap_info);
00221 
00222     if (surface->saved_dc_bitmap) {
00223        SelectObject (surface->dc, surface->saved_dc_bitmap);
00224        surface->saved_dc_bitmap = NULL;
00225     }
00226     
00227     if (surface->bitmap) {
00228        DeleteObject (surface->bitmap);
00229        surface->bitmap = NULL;
00230     }
00231     
00232     if (surface->dc) {
00233        DeleteDC (surface->dc);
00234        surface->dc = NULL;
00235     }
00236  
00237     return status;
00238 }
00239 
00240 static cairo_surface_t *
00241 _cairo_win32_surface_create_for_dc (HDC             original_dc,
00242                                 cairo_format_t  format,
00243                                 int                   width,
00244                                 int                   height)
00245 {
00246     cairo_status_t status;
00247     cairo_win32_surface_t *surface;
00248     char *bits;
00249     int rowstride;
00250 
00251     surface = malloc (sizeof (cairo_win32_surface_t));
00252     if (surface == NULL) {
00253        _cairo_error (CAIRO_STATUS_NO_MEMORY);
00254        return &_cairo_surface_nil;
00255     }
00256 
00257     status = _create_dc_and_bitmap (surface, original_dc, format,
00258                                 width, height,
00259                                 &bits, &rowstride);
00260     if (status)
00261        goto FAIL;
00262 
00263     surface->src_image = cairo_image_surface_create_for_data (bits, format,
00264                                                               width, height, rowstride);
00265     if (surface->src_image->status) {
00266        status = CAIRO_STATUS_NO_MEMORY;
00267        goto FAIL;
00268     }
00269 
00270     surface->dst_image = cairo_image_surface_create_for_data (bits, format,
00271                                                               width, height, rowstride);
00272     if (surface->dst_image->status) {
00273        status = CAIRO_STATUS_NO_MEMORY;
00274        goto FAIL;
00275     }
00276     
00277     surface->format = format;
00278     
00279     surface->clip_rect.x = 0;
00280     surface->clip_rect.y = 0;
00281     surface->clip_rect.width = width;
00282     surface->clip_rect.height = height;
00283 
00284     surface->set_clip = 0;
00285     surface->saved_clip = NULL;
00286     
00287     _cairo_surface_init (&surface->base, &cairo_win32_surface_backend);
00288 
00289     return (cairo_surface_t *)surface;
00290 
00291  FAIL:
00292     if (surface->bitmap) {
00293        SelectObject (surface->dc, surface->saved_dc_bitmap);
00294        DeleteObject (surface->bitmap);
00295         DeleteDC (surface->dc);
00296     }
00297     if (surface)
00298        free (surface);
00299 
00300     if (status == CAIRO_STATUS_NO_MEMORY) {
00301        _cairo_error (CAIRO_STATUS_NO_MEMORY);
00302        return &_cairo_surface_nil;
00303     } else {
00304        _cairo_error (status);
00305        return &_cairo_surface_nil;
00306     }
00307 }
00308 
00309 static cairo_surface_t *
00310 _cairo_win32_surface_create_similar (void     *abstract_src,
00311                                  cairo_content_t content,
00312                                  int           width,
00313                                  int           height)
00314 {
00315     cairo_win32_surface_t *src = abstract_src;
00316     cairo_format_t format = _cairo_format_from_content (content);
00317 
00318     return _cairo_win32_surface_create_for_dc (src->dc, format, width, height);
00319 }
00320 
00334 cairo_surface_t *
00335 _cairo_win32_surface_create_dib (cairo_format_t format,
00336                              int           width,
00337                              int           height)
00338 {
00339     return _cairo_win32_surface_create_for_dc (NULL, format, width, height);
00340 }
00341 
00342 static cairo_status_t
00343 _cairo_win32_surface_finish (void *abstract_surface)
00344 {
00345     cairo_win32_surface_t *surface = abstract_surface;
00346 
00347     if (surface->src_image)
00348        cairo_surface_destroy (surface->src_image);
00349 
00350     if (surface->dst_image)
00351        cairo_surface_destroy (surface->dst_image);
00352 
00353     if (surface->saved_clip) {
00354        DeleteObject (surface->saved_clip);
00355     }
00356 
00357     /* If we created the Bitmap and DC, destroy them */
00358     if (surface->bitmap) {
00359        SelectObject (surface->dc, surface->saved_dc_bitmap);
00360        DeleteObject (surface->bitmap);
00361         DeleteDC (surface->dc);
00362     }
00363 
00364     return CAIRO_STATUS_SUCCESS;
00365 }
00366 
00367 static cairo_status_t
00368 _cairo_win32_surface_get_subimage (cairo_win32_surface_t  *surface,
00369                                int                     x,
00370                                int                     y,
00371                                int                     width,
00372                                int                     height,
00373                                cairo_win32_surface_t **local_out)
00374 {
00375     cairo_win32_surface_t *local;
00376     cairo_status_t status;
00377     cairo_content_t content = _cairo_content_from_format (surface->format);
00378 
00379     local = 
00380        (cairo_win32_surface_t *) _cairo_win32_surface_create_similar (surface,
00381                                                                content,
00382                                                                width,
00383                                                                height);
00384     if (local->base.status)
00385        return CAIRO_STATUS_NO_MEMORY;
00386     
00387     if (!BitBlt (local->dc, 
00388                0, 0,
00389                width, height,
00390                surface->dc,
00391                x, y,
00392                SRCCOPY))
00393        goto FAIL;
00394 
00395     *local_out = local;
00396     
00397     return CAIRO_STATUS_SUCCESS;
00398 
00399  FAIL:
00400     status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_get_subimage");
00401 
00402     if (local)
00403        cairo_surface_destroy (&local->base);
00404 
00405     return status;
00406 }
00407 
00408 static cairo_status_t
00409 _cairo_win32_surface_acquire_source_image (void                    *abstract_surface,
00410                                       cairo_image_surface_t  **image_out,
00411                                       void                   **image_extra)
00412 {
00413     cairo_win32_surface_t *surface = abstract_surface;
00414     cairo_win32_surface_t *local = NULL;
00415     cairo_status_t status;
00416 
00417     if (surface->src_image) {
00418        *image_out = (cairo_image_surface_t *)surface->src_image;
00419        *image_extra = NULL;
00420 
00421        return CAIRO_STATUS_SUCCESS;
00422     }
00423 
00424     status = _cairo_win32_surface_get_subimage (abstract_surface, 0, 0,
00425                                           surface->clip_rect.width,
00426                                           surface->clip_rect.height, &local);
00427     if (status)
00428        return status;
00429 
00430     *image_out = (cairo_image_surface_t *)local->src_image;
00431     *image_extra = local;
00432 
00433     return CAIRO_STATUS_SUCCESS;
00434 }
00435 
00436 static void
00437 _cairo_win32_surface_release_source_image (void                   *abstract_surface,
00438                                       cairo_image_surface_t  *image,
00439                                       void                   *image_extra)
00440 {
00441     cairo_win32_surface_t *local = image_extra;
00442     
00443     if (local)
00444        cairo_surface_destroy ((cairo_surface_t *)local);
00445 }
00446 
00447 static cairo_status_t
00448 _cairo_win32_surface_acquire_dest_image (void                    *abstract_surface,
00449                                     cairo_rectangle_t       *interest_rect,
00450                                     cairo_image_surface_t  **image_out,
00451                                     cairo_rectangle_t       *image_rect,
00452                                     void                   **image_extra)
00453 {
00454     cairo_win32_surface_t *surface = abstract_surface;
00455     cairo_win32_surface_t *local = NULL;
00456     cairo_status_t status;
00457     RECT clip_box;
00458     int x1, y1, x2, y2;
00459         
00460     if (surface->dst_image) {
00461        image_rect->x = 0;
00462        image_rect->y = 0;
00463        image_rect->width = surface->clip_rect.width;
00464        image_rect->height = surface->clip_rect.height;
00465 
00466        *image_out = (cairo_image_surface_t *)surface->dst_image;
00467        *image_extra = NULL;
00468 
00469        return CAIRO_STATUS_SUCCESS;
00470     }
00471 
00472     if (GetClipBox (surface->dc, &clip_box) == ERROR)
00473        return _cairo_win32_print_gdi_error ("_cairo_win3_surface_acquire_dest_image");
00474     
00475     x1 = clip_box.left;
00476     x2 = clip_box.right;
00477     y1 = clip_box.top;
00478     y2 = clip_box.bottom;
00479     
00480     if (interest_rect->x > x1)
00481        x1 = interest_rect->x;
00482     if (interest_rect->y > y1)
00483        y1 = interest_rect->y;
00484     if (interest_rect->x + interest_rect->width < x2)
00485        x2 = interest_rect->x + interest_rect->width;
00486     if (interest_rect->y + interest_rect->height < y2)
00487        y2 = interest_rect->y + interest_rect->height;
00488     
00489     if (x1 >= x2 || y1 >= y2) {
00490        *image_out = NULL;
00491        *image_extra = NULL;
00492        
00493        return CAIRO_STATUS_SUCCESS;
00494     }
00495        
00496     status = _cairo_win32_surface_get_subimage (abstract_surface, 
00497                                           x1, y1, x2 - x1, y2 - y1,
00498                                           &local);
00499     if (status)
00500        return status;
00501 
00502     *image_out = (cairo_image_surface_t *)local->dst_image;
00503     *image_extra = local;
00504     
00505     image_rect->x = x1;
00506     image_rect->y = y1;
00507     image_rect->width = x2 - x1;
00508     image_rect->height = y2 - y1;
00509 
00510     return CAIRO_STATUS_SUCCESS;
00511 }
00512 
00513 static void
00514 _cairo_win32_surface_release_dest_image (void                   *abstract_surface,
00515                                     cairo_rectangle_t      *interest_rect,
00516                                     cairo_image_surface_t  *image,
00517                                     cairo_rectangle_t      *image_rect,
00518                                     void                   *image_extra)
00519 {
00520     cairo_win32_surface_t *surface = abstract_surface;
00521     cairo_win32_surface_t *local = image_extra;
00522     
00523     if (!local)
00524        return;
00525 
00526     if (!BitBlt (surface->dc,
00527                image_rect->x, image_rect->y,
00528                image_rect->width, image_rect->height,
00529                local->dc,
00530                0, 0,
00531                SRCCOPY))
00532        _cairo_win32_print_gdi_error ("_cairo_win32_surface_release_dest_image");
00533 
00534     cairo_surface_destroy ((cairo_surface_t *)local);
00535 }
00536 
00537 #if !defined(AC_SRC_OVER)
00538 #define AC_SRC_OVER                 0x00
00539 #pragma pack(1)
00540 typedef struct {
00541     BYTE   BlendOp;
00542     BYTE   BlendFlags;
00543     BYTE   SourceConstantAlpha;
00544     BYTE   AlphaFormat;
00545 }BLENDFUNCTION;
00546 #pragma pack()
00547 #endif
00548 
00549 /* for compatibility with VC++ 6 */
00550 #ifndef AC_SRC_ALPHA
00551 #define AC_SRC_ALPHA                0x01
00552 #endif
00553 
00554 typedef BOOL (WINAPI *cairo_alpha_blend_func_t) (HDC hdcDest,
00555                                            int nXOriginDest,
00556                                            int nYOriginDest,
00557                                            int nWidthDest,
00558                                            int hHeightDest,
00559                                            HDC hdcSrc,
00560                                            int nXOriginSrc,
00561                                            int nYOriginSrc,
00562                                            int nWidthSrc,
00563                                            int nHeightSrc,
00564                                            BLENDFUNCTION blendFunction);
00565 
00566 static cairo_int_status_t
00567 _composite_alpha_blend (cairo_win32_surface_t *dst,
00568                      cairo_win32_surface_t *src,
00569                      int                    alpha,
00570                      int                    src_x,
00571                      int                    src_y,
00572                      int                    dst_x,
00573                      int                    dst_y,
00574                      int                    width,
00575                      int                    height)
00576 {
00577     static unsigned alpha_blend_checked = FALSE;
00578     static cairo_alpha_blend_func_t alpha_blend = NULL;
00579 
00580     BLENDFUNCTION blend_function;
00581 
00582     /* Check for AlphaBlend dynamically to allow compiling on
00583      * MSVC 6 and use on older windows versions
00584      */
00585     if (!alpha_blend_checked) {
00586        OSVERSIONINFO os;
00587     
00588        os.dwOSVersionInfoSize = sizeof (os);
00589        GetVersionEx (&os);
00590        
00591        /* If running on Win98, disable using AlphaBlend()
00592         * to avoid Win98 AlphaBlend() bug */
00593        if (VER_PLATFORM_WIN32_WINDOWS != os.dwPlatformId ||
00594            os.dwMajorVersion != 4 || os.dwMinorVersion != 10)
00595        {
00596            HMODULE msimg32_dll = LoadLibrary ("msimg32");
00597            
00598            if (msimg32_dll != NULL)
00599               alpha_blend = (cairo_alpha_blend_func_t)GetProcAddress (msimg32_dll,
00600                                                                "AlphaBlend");
00601        }
00602            
00603        alpha_blend_checked = TRUE;
00604     }
00605 
00606     if (alpha_blend == NULL)
00607        return CAIRO_INT_STATUS_UNSUPPORTED;
00608     
00609     blend_function.BlendOp = AC_SRC_OVER;
00610     blend_function.BlendFlags = 0;
00611     blend_function.SourceConstantAlpha = alpha;
00612     blend_function.AlphaFormat = src->format == CAIRO_FORMAT_ARGB32 ? AC_SRC_ALPHA : 0;
00613 
00614     if (!alpha_blend (dst->dc,
00615                     dst_x, dst_y,
00616                     width, height,
00617                     src->dc,
00618                     src_x, src_y,
00619                     width, height,
00620                     blend_function))
00621        return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite");
00622     
00623     return CAIRO_STATUS_SUCCESS;
00624 }
00625 
00626 static cairo_int_status_t
00627 _cairo_win32_surface_composite (cairo_operator_t operator,
00628                             cairo_pattern_t             *pattern,
00629                             cairo_pattern_t             *mask_pattern,
00630                             void                 *abstract_dst,
00631                             int                  src_x,
00632                             int                  src_y,
00633                             int                  mask_x,
00634                             int                  mask_y,
00635                             int                  dst_x,
00636                             int                  dst_y,
00637                             unsigned int         width,
00638                             unsigned int         height)
00639 {
00640     cairo_win32_surface_t *dst = abstract_dst;
00641     cairo_win32_surface_t *src;
00642     cairo_surface_pattern_t *src_surface_pattern;
00643     int alpha;
00644     int integer_transform;
00645     int itx, ity;
00646 
00647     if (pattern->type != CAIRO_PATTERN_SURFACE ||
00648        pattern->extend != CAIRO_EXTEND_NONE)
00649        return CAIRO_INT_STATUS_UNSUPPORTED;
00650 
00651     if (mask_pattern) {
00652        /* FIXME: When we fully support RENDER style 4-channel
00653         * masks we need to check r/g/b != 1.0.
00654         */
00655        if (mask_pattern->type != CAIRO_PATTERN_SOLID)
00656            return CAIRO_INT_STATUS_UNSUPPORTED;
00657 
00658        alpha = ((cairo_solid_pattern_t *)mask_pattern)->color.alpha_short >> 8;
00659     } else {
00660         alpha = 255;
00661     }
00662 
00663     src_surface_pattern = (cairo_surface_pattern_t *)pattern;
00664     src = (cairo_win32_surface_t *)src_surface_pattern->surface;
00665 
00666     if (src->base.backend != dst->base.backend)
00667        return CAIRO_INT_STATUS_UNSUPPORTED;
00668     
00669     integer_transform = _cairo_matrix_is_integer_translation (&pattern->matrix, &itx, &ity);
00670     if (!integer_transform)
00671        return CAIRO_INT_STATUS_UNSUPPORTED;
00672 
00673     if (alpha == 255 &&
00674        src->format == dst->format &&
00675        (operator == CAIRO_OPERATOR_SOURCE ||
00676         (src->format == CAIRO_FORMAT_RGB24 && operator == CAIRO_OPERATOR_OVER))) {
00677        
00678        if (!BitBlt (dst->dc,
00679                    dst_x, dst_y,
00680                    width, height,
00681                    src->dc,
00682                    src_x + itx, src_y + ity,
00683                    SRCCOPY))
00684            return _cairo_win32_print_gdi_error ("_cairo_win32_surface_composite");
00685 
00686        return CAIRO_STATUS_SUCCESS;
00687        
00688     } else if (integer_transform &&
00689               (src->format == CAIRO_FORMAT_RGB24 || src->format == CAIRO_FORMAT_ARGB32) &&
00690               dst->format == CAIRO_FORMAT_RGB24 &&
00691               operator == CAIRO_OPERATOR_OVER) {
00692 
00693        return _composite_alpha_blend (dst, src, alpha,
00694                                    src_x + itx, src_y + ity,
00695                                    dst_x, dst_y, width, height);
00696     }
00697     
00698     return CAIRO_INT_STATUS_UNSUPPORTED;
00699 }
00700 
00701 /* This big function tells us how to optimize operators for the
00702  * case of solid destination and constant-alpha source
00703  *
00704  * NOTE: This function needs revisiting if we add support for
00705  *       super-luminescent colors (a == 0, r,g,b > 0)
00706  */
00707 static enum { DO_CLEAR, DO_SOURCE, DO_NOTHING, DO_UNSUPPORTED }
00708 categorize_solid_dest_operator (cairo_operator_t operator,
00709                             unsigned short   alpha)
00710 {
00711     enum { SOURCE_TRANSPARENT, SOURCE_LIGHT, SOURCE_SOLID, SOURCE_OTHER } source;
00712 
00713     if (alpha >= 0xff00)
00714        source = SOURCE_SOLID;
00715     else if (alpha < 0x100)
00716        source = SOURCE_TRANSPARENT;
00717     else
00718        source = SOURCE_OTHER;
00719     
00720     switch (operator) {
00721     case CAIRO_OPERATOR_CLEAR:    /* 0                 0 */
00722     case CAIRO_OPERATOR_OUT:      /* 1 - Ab            0 */
00723        return DO_CLEAR;
00724        break;
00725        
00726     case CAIRO_OPERATOR_SOURCE:   /* 1                 0 */
00727     case CAIRO_OPERATOR_IN:       /* Ab                0 */
00728        return DO_SOURCE;
00729        break;
00730 
00731     case CAIRO_OPERATOR_OVER:     /* 1            1 - Aa */
00732     case CAIRO_OPERATOR_ATOP:     /* Ab           1 - Aa */
00733        if (source == SOURCE_SOLID)
00734            return DO_SOURCE;
00735        else if (source == SOURCE_TRANSPARENT)
00736            return DO_NOTHING;
00737        else
00738            return DO_UNSUPPORTED;
00739        break;
00740        
00741     case CAIRO_OPERATOR_DEST_OUT: /* 0            1 - Aa */
00742     case CAIRO_OPERATOR_XOR:      /* 1 - Ab       1 - Aa */
00743        if (source == SOURCE_SOLID)
00744            return DO_CLEAR;
00745        else if (source == SOURCE_TRANSPARENT)
00746            return DO_NOTHING;
00747        else
00748            return DO_UNSUPPORTED;
00749        break;
00750        
00751     case CAIRO_OPERATOR_DEST:     /* 0                 1 */
00752     case CAIRO_OPERATOR_DEST_OVER:/* 1 - Ab            1 */
00753     case CAIRO_OPERATOR_SATURATE: /* min(1,(1-Ab)/Aa)  1 */
00754        return DO_NOTHING;
00755        break;
00756 
00757     case CAIRO_OPERATOR_DEST_IN:  /* 0                Aa */
00758     case CAIRO_OPERATOR_DEST_ATOP:/* 1 - Ab           Aa */
00759        if (source == SOURCE_SOLID)
00760            return DO_NOTHING;
00761        else if (source == SOURCE_TRANSPARENT)
00762            return DO_CLEAR;
00763        else
00764            return DO_UNSUPPORTED;
00765        break;
00766        
00767     case CAIRO_OPERATOR_ADD:         /* 1                1 */
00768        if (source == SOURCE_TRANSPARENT)
00769            return DO_NOTHING;
00770        else
00771            return DO_UNSUPPORTED;
00772        break;
00773     }  
00774 
00775     ASSERT_NOT_REACHED;
00776     return DO_UNSUPPORTED;
00777 }
00778 
00779 static cairo_int_status_t
00780 _cairo_win32_surface_fill_rectangles (void                     *abstract_surface,
00781                                   cairo_operator_t             operator,
00782                                   const cairo_color_t   *color,
00783                                   cairo_rectangle_t            *rects,
00784                                   int                   num_rects)
00785 {
00786     cairo_win32_surface_t *surface = abstract_surface;
00787     cairo_status_t status;
00788     COLORREF new_color;
00789     HBRUSH new_brush;
00790     int i;
00791 
00792     /* If we have a local image, use the fallback code; it will be as fast
00793      * as calling out to GDI.
00794      */
00795     if (surface->dst_image)
00796        return CAIRO_INT_STATUS_UNSUPPORTED;
00797 
00798     /* Optimize for no destination alpha (surface->pixman_image is non-NULL for all
00799      * surfaces with alpha.)
00800      */
00801     switch (categorize_solid_dest_operator (operator, color->alpha_short)) {
00802     case DO_CLEAR:
00803        new_color = RGB (0, 0, 0);
00804        break; 
00805     case DO_SOURCE:
00806        new_color = RGB (color->red_short >> 8, color->green_short >> 8, color->blue_short >> 8);
00807        break;
00808     case DO_NOTHING:
00809        return CAIRO_STATUS_SUCCESS;
00810     case DO_UNSUPPORTED:
00811     default:
00812        return CAIRO_INT_STATUS_UNSUPPORTED;
00813     }
00814     
00815     new_brush = CreateSolidBrush (new_color);
00816     if (!new_brush)
00817        return _cairo_win32_print_gdi_error ("_cairo_win32_surface_fill_rectangles");
00818     
00819     for (i = 0; i < num_rects; i++) {
00820        RECT rect;
00821 
00822        rect.left = rects[i].x;
00823        rect.top = rects[i].y;
00824        rect.right = rects[i].x + rects[i].width;
00825        rect.bottom = rects[i].y + rects[i].height;
00826 
00827        if (!FillRect (surface->dc, &rect, new_brush))
00828            goto FAIL;
00829     }
00830 
00831     DeleteObject (new_brush);
00832     
00833     return CAIRO_STATUS_SUCCESS;
00834 
00835  FAIL:
00836     status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_fill_rectangles");
00837     
00838     DeleteObject (new_brush);
00839     
00840     return status;
00841 }
00842 
00843 static cairo_int_status_t
00844 _cairo_win32_surface_set_clip_region (void              *abstract_surface,
00845                                   pixman_region16_t *region)
00846 {
00847     cairo_win32_surface_t *surface = abstract_surface;
00848     cairo_status_t status;
00849 
00850     /* If we are in-memory, then we set the clip on the image surface
00851      * as well as on the underlying GDI surface.
00852      */
00853     if (surface->dst_image) {
00854        unsigned int serial;
00855 
00856        serial = _cairo_surface_allocate_clip_serial (surface->dst_image);
00857        _cairo_surface_set_clip_region (surface->dst_image, region, serial);
00858     }
00859 
00860     /* The semantics we want is that any clip set by cairo combines
00861      * is intersected with the clip on device context that the
00862      * surface was created for. To implement this, we need to
00863      * save the original clip when first setting a clip on surface.
00864      */
00865 
00866     if (region == NULL) {
00867        /* Clear any clip set by cairo, return to the original */
00868        
00869        if (surface->set_clip) {
00870            if (SelectClipRgn (surface->dc, surface->saved_clip) == ERROR)
00871               return _cairo_win32_print_gdi_error ("_cairo_win32_surface_set_clip_region");
00872 
00873            if (surface->saved_clip) {
00874               DeleteObject (surface->saved_clip);
00875               surface->saved_clip = NULL;
00876            }
00877 
00878            surface->set_clip = 0;
00879        }
00880            
00881        return CAIRO_STATUS_SUCCESS;
00882     
00883     } else {
00884        pixman_box16_t *boxes = pixman_region_rects (region);
00885        int num_boxes = pixman_region_num_rects (region);
00886        pixman_box16_t *extents = pixman_region_extents (region);
00887        RGNDATA *data;
00888        size_t data_size;
00889        RECT *rects;
00890        int i;
00891        HRGN gdi_region;
00892 
00893        /* Create a GDI region for the cairo region */
00894 
00895        data_size = sizeof (RGNDATAHEADER) + num_boxes * sizeof (RECT);
00896        data = malloc (data_size);
00897        if (!data)
00898            return CAIRO_STATUS_NO_MEMORY;
00899        rects = (RECT *)data->Buffer;
00900 
00901        data->rdh.dwSize = sizeof (RGNDATAHEADER);
00902        data->rdh.iType = RDH_RECTANGLES;
00903        data->rdh.nCount = num_boxes;
00904        data->rdh.nRgnSize = num_boxes * sizeof (RECT);
00905        data->rdh.rcBound.left = extents->x1;
00906        data->rdh.rcBound.top = extents->y1;
00907        data->rdh.rcBound.right = extents->x2;
00908        data->rdh.rcBound.bottom = extents->y2;
00909 
00910        for (i = 0; i < num_boxes; i++) {
00911            rects[i].left = boxes[i].x1;
00912            rects[i].top = boxes[i].y1;
00913            rects[i].right = boxes[i].x2;
00914            rects[i].bottom = boxes[i].y2;
00915        }
00916 
00917        gdi_region = ExtCreateRegion (NULL, data_size, data);
00918        free (data);
00919        
00920        if (!gdi_region)
00921            return CAIRO_STATUS_NO_MEMORY;
00922 
00923        if (surface->set_clip) {
00924            /* Combine the new region with the original clip */
00925            
00926            if (surface->saved_clip) {
00927               if (CombineRgn (gdi_region, gdi_region, surface->saved_clip, RGN_AND) == ERROR)
00928                   goto FAIL;
00929            }
00930 
00931            if (SelectClipRgn (surface->dc, gdi_region) == ERROR)
00932               goto FAIL;
00933               
00934        } else {
00935            /* Save the the current region */
00936 
00937            surface->saved_clip = CreateRectRgn (0, 0, 0, 0);
00938            if (!surface->saved_clip) {
00939               goto FAIL;        }
00940 
00941            /* This function has no error return! */
00942            if (GetClipRgn (surface->dc, surface->saved_clip) == 0) { /* No clip */
00943               DeleteObject (surface->saved_clip);
00944               surface->saved_clip = NULL;
00945            }
00946               
00947            if (ExtSelectClipRgn (surface->dc, gdi_region, RGN_AND) == ERROR)
00948               goto FAIL;
00949 
00950            surface->set_clip = 1;
00951        }
00952 
00953        DeleteObject (gdi_region);
00954        return CAIRO_STATUS_SUCCESS;
00955 
00956     FAIL:
00957        status = _cairo_win32_print_gdi_error ("_cairo_win32_surface_set_clip_region");
00958        DeleteObject (gdi_region);
00959        return status;
00960     }
00961 }
00962 
00963 static cairo_int_status_t
00964 _cairo_win32_surface_get_extents (void               *abstract_surface,
00965                               cairo_rectangle_t *rectangle)
00966 {
00967     cairo_win32_surface_t *surface = abstract_surface;
00968     RECT clip_box;
00969 
00970     if (GetClipBox (surface->dc, &clip_box) == ERROR)
00971        return _cairo_win32_print_gdi_error ("_cairo_win3_surface_acquire_dest_image");
00972 
00973     rectangle->x = clip_box.left;
00974     rectangle->y = clip_box.top;
00975     rectangle->width  = clip_box.right  - clip_box.left;
00976     rectangle->height = clip_box.bottom - clip_box.top;
00977 
00978     return CAIRO_STATUS_SUCCESS;
00979 }
00980 
00981 static cairo_status_t
00982 _cairo_win32_surface_flush (void *abstract_surface)
00983 {
00984     return _cairo_surface_reset_clip (abstract_surface);
00985 }
00986 
00987 cairo_surface_t *
00988 cairo_win32_surface_create (HDC hdc)
00989 {
00990     cairo_win32_surface_t *surface;
00991     RECT rect;
00992 
00993     /* Try to figure out the drawing bounds for the Device context
00994      */
00995     if (GetClipBox (hdc, &rect) == ERROR) {
00996        _cairo_win32_print_gdi_error ("cairo_win32_surface_create");
00997        /* XXX: Can we make a more reasonable guess at the error cause here? */
00998        _cairo_error (CAIRO_STATUS_NO_MEMORY);
00999        return &_cairo_surface_nil;
01000     }
01001     
01002     surface = malloc (sizeof (cairo_win32_surface_t));
01003     if (surface == NULL) {
01004        _cairo_error (CAIRO_STATUS_NO_MEMORY);
01005        return &_cairo_surface_nil;
01006     }
01007 
01008     surface->src_image = NULL;
01009     surface->dst_image = NULL;
01010     surface->format = CAIRO_FORMAT_RGB24;
01011     
01012     surface->dc = hdc;
01013     surface->bitmap = NULL;
01014     surface->saved_dc_bitmap = NULL;
01015     
01016     surface->clip_rect.x = rect.left;
01017     surface->clip_rect.y = rect.top;
01018     surface->clip_rect.width = rect.right - rect.left;
01019     surface->clip_rect.height = rect.bottom - rect.top;
01020 
01021     surface->set_clip = 0;
01022     surface->saved_clip = NULL;
01023 
01024     _cairo_surface_init (&surface->base, &cairo_win32_surface_backend);
01025 
01026     return (cairo_surface_t *)surface;
01027 }
01028 
01037 int
01038 _cairo_surface_is_win32 (cairo_surface_t *surface)
01039 {
01040     return surface->backend == &cairo_win32_surface_backend;
01041 }
01042 
01043 static const cairo_surface_backend_t cairo_win32_surface_backend = {
01044     _cairo_win32_surface_create_similar,
01045     _cairo_win32_surface_finish,
01046     _cairo_win32_surface_acquire_source_image,
01047     _cairo_win32_surface_release_source_image,
01048     _cairo_win32_surface_acquire_dest_image,
01049     _cairo_win32_surface_release_dest_image,
01050     NULL, /* clone_similar */
01051     _cairo_win32_surface_composite,
01052     _cairo_win32_surface_fill_rectangles,
01053     NULL, /* composite_trapezoids */
01054     NULL, /* copy_page */
01055     NULL, /* show_page */
01056     _cairo_win32_surface_set_clip_region,
01057     NULL, /* intersect_clip_path */
01058     _cairo_win32_surface_get_extents,
01059     NULL, /* show_glyphs */
01060     NULL, /* fill_path */
01061     NULL, /* get_font_options */
01062     _cairo_win32_surface_flush,
01063     NULL  /* mark_dirty_rectangle */
01064 };
01065 
01066 /*
01067  * Without pthread, on win32 we need to initialize all the 'mutex'es
01068  * before use. It is guaranteed that DllMain will get called single
01069  * threaded before any other function.
01070  * Initializing more than finally needed should not matter much.
01071  */
01072 #ifndef HAVE_PTHREAD_H
01073 CRITICAL_SECTION cairo_toy_font_face_hash_table_mutex;
01074 CRITICAL_SECTION cairo_scaled_font_map_mutex;
01075 CRITICAL_SECTION cairo_ft_unscaled_font_map_mutex;
01076 CRITICAL_SECTION _global_image_glyph_cache_mutex;
01077 
01078 #if 0
01079 BOOL WINAPI
01080 DllMain (HINSTANCE hinstDLL,
01081         DWORD     fdwReason,
01082         LPVOID    lpvReserved)
01083 {
01084   switch (fdwReason)
01085   {
01086   case DLL_PROCESS_ATTACH:
01087     /* every 'mutex' from CAIRO_MUTEX_DECALRE needs to be initialized here */
01088     InitializeCriticalSection (&cairo_toy_font_face_hash_table_mutex);
01089     InitializeCriticalSection (&cairo_scaled_font_map_mutex);
01090     InitializeCriticalSection (&cairo_ft_unscaled_font_map_mutex);
01091     InitializeCriticalSection (&_global_image_glyph_cache_mutex);
01092     break;
01093   case DLL_PROCESS_DETACH:
01094     DeleteCriticalSection (&cairo_toy_font_face_hash_table_mutex);
01095     DeleteCriticalSection (&cairo_scaled_font_map_mutex);
01096     DeleteCriticalSection (&cairo_ft_unscaled_font_map_mutex);
01097     DeleteCriticalSection (&_global_image_glyph_cache_mutex);
01098     break;
01099   }
01100   return TRUE;
01101 }
01102 #endif
01103 
01104 #endif