root/modules/gdip_raster/gdip_rend.cpp

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. gdip_create_path
  2. gdip_new_surface
  3. gdip_delete_surface
  4. gdip_attach_surface_to_device
  5. gdip_attach_surface_to_texture
  6. gdip_attach_surface_to_buffer
  7. gdip_detach_surface
  8. gdip_surface_set_raster_level
  9. gdip_surface_set_matrix
  10. gdip_surface_set_clipper
  11. gdip_get_brush
  12. gdip_setup_path
  13. gdip_surface_set_path
  14. gdip_surface_fill
  15. gdip_surface_flush
  16. gdip_surface_clear
  17. gdip_init_driver_surface
  18. gdip_LoadRenderer
  19. gdip_ShutdownRenderer

/*
 *                      GPAC - Multimedia Framework C SDK
 *
 *                      Authors: Jean Le Feuvre
 *                      Copyright (c) Telecom ParisTech 2000-2012
 *                                      All rights reserved
 *
 *  This file is part of GPAC / GDIplus rasterizer module
 *
 *  GPAC 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, or (at your option)
 *  any later version.
 *
 *  GPAC 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; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */


#include <windows.h>
#include "gdip_priv.h"

/*all GDIPLUS includes for C api*/

struct IDirectDrawSurface7;

#include "GdiplusMem.h"
#include "GdiplusEnums.h"
#include "GdiplusTypes.h"
#include "GdiplusInit.h"
#include "GdiplusPixelFormats.h"
#include "GdiplusColor.h"
#include "GdiplusMetaHeader.h"
#include "GdiplusImaging.h"
#include "GdiplusColorMatrix.h"
#include "GdiplusGpStubs.h"
#include "GdiplusColor.h"
#include "GdiplusFlat.h"

struct _graphics
{
        GpGraphics *graph;
        GpMatrix *mat;
        GpPath *clip;
        GpPath *current;
        u32 w, h;
        Bool center_coords;

        /*offscreen buffer handling*/
        GpBitmap *pBitmap;
};

GpPath *gdip_create_path(GF_Path *_this)
{
        GpPath *p;
        u32 j, i, nb_pts, cur;
        if (!_this || !_this->n_points) return NULL;
        GdipCreatePath(FillModeAlternate, &p);

        GdipSetPathFillMode(p, (_this->flags & GF_PATH_FILL_ZERO_NONZERO) ? FillModeWinding : FillModeAlternate);

        cur = 0;
        for (i=0; i<_this->n_contours; i++) {
                nb_pts = 1+_this->contours[i] - cur;
                GdipStartPathFigure(p);
                for (j=cur+1; j<cur+nb_pts; ) {
                        switch (_this->tags[j]) {
                        case GF_PATH_CURVE_ON:
                                GdipAddPathLine(p, FIX2FLT(_this->points[j-1].x), FIX2FLT(_this->points[j-1].y), FIX2FLT(_this->points[j].x), FIX2FLT(_this->points[j].y));
                                j++;
                                break;
                        case GF_PATH_CLOSE:
                                GdipAddPathLine(p, FIX2FLT(_this->points[j].x), FIX2FLT(_this->points[j].y), FIX2FLT(_this->points[cur].x), FIX2FLT(_this->points[cur].y));
                                j++;
                                break;
                        case GF_PATH_CURVE_CUBIC:
                                GdipAddPathBezier(p,
                                                  FIX2FLT(_this->points[j-1].x), FIX2FLT(_this->points[j-1].y),
                                                  FIX2FLT(_this->points[j].x), FIX2FLT(_this->points[j].y),
                                                  FIX2FLT(_this->points[j+1].x), FIX2FLT(_this->points[j+1].y),
                                                  FIX2FLT(_this->points[j+2].x), FIX2FLT(_this->points[j+2].y)
                                                 );
                                j+=3;
                                break;
                        case GF_PATH_CURVE_CONIC:
                        {
                                GF_Point2D ctl, end, c1, c2, start;
                                start = _this->points[j-1];
                                ctl = _this->points[j];
                                end = _this->points[j+1];
                                c1.x = start.x + 2*(ctl.x - start.x) / 3;
                                c1.y = start.y + 2*(ctl.y - start.y) / 3;
                                c2.x = c1.x + (end.x - start.x) / 3;
                                c2.y = c1.y + (end.y - start.y) / 3;
                                GdipAddPathBezier(p,
                                                  FIX2FLT(start.x), FIX2FLT(start.y),
                                                  FIX2FLT(c1.x), FIX2FLT(c1.y),
                                                  FIX2FLT(c2.x), FIX2FLT(c2.y),
                                                  FIX2FLT(end.x), FIX2FLT(end.y)
                                                 );
                                j+=2;
                        }
                        break;
                        }
                }
                GdipClosePathFigure(p);
                cur += nb_pts;
        }
        return p;
}

#define GPGRAPH() struct _graphics *_graph = (struct _graphics *)_this;

static
GF_SURFACE gdip_new_surface(GF_Raster2D *, Bool center_coords)
{
        struct _graphics *graph;
        SAFEALLOC(graph, struct _graphics);
        graph->center_coords = center_coords;
        return graph;
}

static
void gdip_delete_surface(GF_SURFACE _this)
{
        GPGRAPH();
        gf_free(_graph);
}

/*should give the best results with the clippers*/
#define GDIP_PIXEL_MODE PixelOffsetModeHighQuality

static
GF_Err gdip_attach_surface_to_device(GF_SURFACE _this, void *os_handle, u32 width, u32 height)
{
        GpMatrix *mat;
        HDC handle = (HDC) os_handle;
        GPGRAPH();
        if (!_graph || !handle) return GF_BAD_PARAM;
        if (_graph->graph) return GF_BAD_PARAM;
        GdipCreateFromHDC(handle, &_graph->graph);

        GdipCreateMatrix(&mat);
        if (    _graph->center_coords) {
                GdipScaleMatrix(mat, 1.0, -1.0, MatrixOrderAppend);
                GdipTranslateMatrix(mat, (Float) width/2, (Float) height/2, MatrixOrderAppend);
        }
        GdipSetWorldTransform(_graph->graph, mat);
        GdipDeleteMatrix(mat);
        _graph->w = width;
        _graph->h = height;
        GdipSetPixelOffsetMode(_graph->graph, GDIP_PIXEL_MODE);
        return GF_OK;
}
static
GF_Err gdip_attach_surface_to_texture(GF_SURFACE _this, GF_STENCIL sten)
{
        GpMatrix *mat;
        struct _stencil *_sten = (struct _stencil *)sten;
        GPGRAPH();
        if (!_graph || !_sten || !_sten->pBitmap) return GF_BAD_PARAM;

        GdipGetImageGraphicsContext(_sten->pBitmap, &_graph->graph);

        if (_graph->center_coords) {
                GdipCreateMatrix(&mat);
                GdipScaleMatrix(mat, 1.0, -1.0, MatrixOrderAppend);
                GdipTranslateMatrix(mat, (Float) _sten->width/2, (Float) _sten->height/2, MatrixOrderAppend);
                GdipSetWorldTransform(_graph->graph, mat);
                GdipDeleteMatrix(mat);
        }
        _graph->w = _sten->width;
        _graph->h = _sten->height;
        GdipSetPixelOffsetMode(_graph->graph, GDIP_PIXEL_MODE);
        return GF_OK;
}
static
GF_Err gdip_attach_surface_to_buffer(GF_SURFACE _this, char *pixels, u32 width, u32 height, s32 pitch_x, s32 pitch_y, GF_PixelFormat pixelFormat)
{
        GpMatrix *mat;
        u32 pFormat;
        GPGRAPH();

        if (pitch_y%4) return GF_NOT_SUPPORTED;

        switch (pixelFormat) {
        case GF_PIXEL_ALPHAGREY:
                pFormat = PixelFormat16bppGrayScale;
                if (pitch_x != 2) return GF_NOT_SUPPORTED;
                break;
        case GF_PIXEL_RGB_555:
                pFormat = PixelFormat16bppRGB555;
                if (pitch_x != 2) return GF_NOT_SUPPORTED;
                break;
        case GF_PIXEL_RGB_565:
                pFormat = PixelFormat16bppRGB565;
                if (pitch_x != 2) return GF_NOT_SUPPORTED;
                break;
        case GF_PIXEL_RGB_24:
                pFormat = PixelFormat24bppRGB;
                if (pitch_x != 3) return GF_NOT_SUPPORTED;
                break;
        case GF_PIXEL_RGB_32:
                pFormat = PixelFormat32bppRGB;
                if (pitch_x != 4) return GF_NOT_SUPPORTED;
                break;
        case GF_PIXEL_ARGB:
                pFormat = PixelFormat32bppARGB;
                if (pitch_x != 4) return GF_NOT_SUPPORTED;
                break;
        default:
                return GF_NOT_SUPPORTED;
        }
        GdipCreateBitmapFromScan0(width, height, pitch_y, pFormat, (unsigned char*)pixels, &_graph->pBitmap);
        GdipGetImageGraphicsContext(_graph->pBitmap, &_graph->graph);

        _graph->w = width;
        _graph->h = height;
        if (_graph->center_coords) {
                GdipCreateMatrix(&mat);
                GdipScaleMatrix(mat, 1.0, -1.0, MatrixOrderAppend);
                GdipTranslateMatrix(mat, (Float) width/2, (Float) height/2, MatrixOrderAppend);
                GdipSetWorldTransform(_graph->graph, mat);
                GdipDeleteMatrix(mat);
        }
        GdipSetPixelOffsetMode(_graph->graph, GDIP_PIXEL_MODE);
        return GF_OK;
}

static
void gdip_detach_surface(GF_SURFACE _this)
{
        GPGRAPH();
        if (_graph->graph) GdipDeleteGraphics(_graph->graph);
        _graph->graph = NULL;
        if (_graph->clip) GdipDeletePath(_graph->clip);
        _graph->clip = NULL;
        if (_graph->pBitmap) GdipDisposeImage(_graph->pBitmap);
        _graph->pBitmap = NULL;
        if (_graph->current) GdipDeletePath(_graph->current);
        _graph->current = NULL;
}


static
GF_Err gdip_surface_set_raster_level(GF_SURFACE _this, GF_RasterLevel RasterSetting)
{
        GPGRAPH();
        switch (RasterSetting) {
        case GF_RASTER_HIGH_SPEED:
                GdipSetSmoothingMode(_graph->graph, SmoothingModeHighSpeed);
                GdipSetCompositingQuality(_graph->graph, CompositingQualityHighSpeed);
                break;
        case GF_RASTER_MID:
                GdipSetSmoothingMode(_graph->graph, SmoothingModeDefault);
                GdipSetCompositingQuality(_graph->graph, CompositingQualityDefault);
                break;
        case GF_RASTER_HIGH_QUALITY:
                GdipSetSmoothingMode(_graph->graph, SmoothingModeHighQuality);
                GdipSetCompositingQuality(_graph->graph, CompositingQualityDefault);
                /*THIS IS HORRIBLY SLOW DON'T EVEN THINK ABOUT IT*/
                /*GdipSetCompositingQuality(_graph->graph, CompositingQualityHighQuality);*/
                break;
        }
        return GF_OK;
}
static
GF_Err gdip_surface_set_matrix(GF_SURFACE _this, GF_Matrix2D * mat)
{
        GPGRAPH();
        if (_graph->mat) GdipDeleteMatrix(_graph->mat);

        _graph->mat = mat_gpac_to_gdip(mat);
        return GF_OK;
}
static
GF_Err gdip_surface_set_clipper(GF_SURFACE _this, GF_IRect *rc)
{
        GPGRAPH();
        if (_graph->clip) GdipDeletePath(_graph->clip);
        _graph->clip = 0L;
        if (!rc) return GF_OK;

        GdipCreatePath(FillModeAlternate, &_graph->clip);
        GdipAddPathRectangleI(_graph->clip, rc->x, rc->y - rc->height, rc->width, rc->height);
        return GF_OK;
}

static
GpBrush *gdip_get_brush(struct _stencil *_sten)
{
        if (_sten->pSolid) return _sten->pSolid;
        if (_sten->pLinear) return _sten->pLinear;
        if (_sten->pRadial) return _sten->pRadial;
        if (_sten->pTexture) return _sten->pTexture;
        return NULL;
}

static GpPath *gdip_setup_path(struct _graphics *_this, GF_Path *path)
{
        GpPath *tr = gdip_create_path(path);
        /*append current matrix*/
        if (_this->mat) GdipTransformPath(tr, _this->mat);
        return tr;
}

static
GF_Err gdip_surface_set_path(GF_SURFACE _this, GF_Path *path)
{
        struct _storepath *_path;
        GPGRAPH();
        if (!_graph) return GF_BAD_PARAM;
        if (_graph->current) GdipDeletePath(_graph->current);
        _graph->current = NULL;
        if (!path) return GF_OK;

        _path = (struct _storepath *)path;
        _graph->current = gdip_setup_path(_graph, path);
        return GF_OK;
}

//#define NODRAW

static
GF_Err gdip_surface_fill(GF_SURFACE _this, GF_STENCIL stencil)
{
        GpStatus ret;
        GpMatrix *newmat;
        struct _stencil *_sten;
        GPGRAPH();
        if (!_this) return GF_BAD_PARAM;
        if (!_graph->current) return GF_OK;
        _sten = (struct _stencil *)stencil;
        assert(_sten);

#ifdef NODRAW
        return GF_OK;
#endif


        if (_graph->clip) GdipSetClipPath(_graph->graph, _graph->clip, CombineModeReplace);

        switch (_sten->type) {
        case GF_STENCIL_SOLID:
                assert(_sten->pSolid);
                GdipFillPath(_graph->graph, _sten->pSolid, _graph->current);
                break;
        case GF_STENCIL_LINEAR_GRADIENT:
                if (_sten->pMat) {
                        /*rebuild gradient*/
                        gdip_recompute_line_gradient(_sten);

                        GdipResetTextureTransform((GpTexture*)_sten->pLinear);
                        if (_sten->pMat) {
                                GdipCloneMatrix(_sten->pMat, &newmat);
                        } else {
                                GdipCreateMatrix(&newmat);
                        }
                        GdipMultiplyMatrix(newmat, _sten->pLinearMat, MatrixOrderPrepend);
                        GdipSetTextureTransform((GpTexture*)_sten->pLinear, newmat);
                        GdipDeleteMatrix(newmat);
                }
                GdipFillPath(_graph->graph, _sten->pLinear, _graph->current);
                break;
        case GF_STENCIL_RADIAL_GRADIENT:
                /*build gradient*/
                gdip_recompute_radial_gradient(_sten);

                GdipSetCompositingQuality(_graph->graph, CompositingQualityHighSpeed);
                GdipSetInterpolationMode(_graph->graph, InterpolationModeLowQuality);
                GdipSetSmoothingMode(_graph->graph, SmoothingModeHighSpeed);

                /*check if we need to draw solid background (GDIplus doesn't implement padded mode on path gradients)*/
                if (_sten->pSolid) {
                        GpPath *tr;
                        GdipClonePath(_sten->circle, &tr);
                        GdipTransformPath(tr, _sten->pMat);
                        GdipSetClipPath(_graph->graph, tr, CombineModeExclude);
                        GdipFillPath(_graph->graph, _sten->pSolid, _graph->current);
                        GdipDeletePath(tr);
                        GdipResetClip(_graph->graph);
                        if (_graph->clip) GdipSetClipPath(_graph->graph, _graph->clip, CombineModeReplace);
                }
                GdipFillPath(_graph->graph, _sten->pRadial, _graph->current);
                break;
        case GF_STENCIL_VERTEX_GRADIENT:
                assert(_sten->pRadial);
                if (_sten->pMat) GdipSetTextureTransform((GpTexture*)_sten->pRadial, _sten->pMat);
                ret = GdipFillPath(_graph->graph, _sten->pRadial, _graph->current);
                break;
        case GF_STENCIL_TEXTURE:
                gdip_load_texture(_sten);
                if (_sten->pTexture) {
                        GpMatrix *newmat;
                        GdipResetTextureTransform((GpTexture*)_sten->pTexture);
                        if (_sten->pMat) {
                                GdipCloneMatrix(_sten->pMat, &newmat);
                        } else {
                                GdipCreateMatrix(&newmat);
                        }
                        /*gdip flip*/
                        if (_graph->center_coords && !(_sten->tiling&GF_TEXTURE_FLIP) )
                                GdipScaleMatrix(newmat, 1, -1, MatrixOrderPrepend);
                        else if (!_graph->center_coords && (_sten->tiling&GF_TEXTURE_FLIP) )
                                GdipScaleMatrix(newmat, 1, -1, MatrixOrderPrepend);

                        GdipSetTextureTransform((GpTexture*)_sten->pTexture, newmat);
                        GdipDeleteMatrix(newmat);

                        GdipSetInterpolationMode(_graph->graph, (_sten->tFilter==GF_TEXTURE_FILTER_HIGH_QUALITY) ? InterpolationModeHighQuality : InterpolationModeLowQuality);
                        GdipFillPath(_graph->graph, _sten->pTexture, _graph->current);
                }
                break;
        }
        return GF_OK;
}


static
GF_Err gdip_surface_flush(GF_SURFACE _this)
{
        GPGRAPH();
        GdipFlush(_graph->graph, FlushIntentionSync);
        return GF_OK;
}

static
GF_Err gdip_surface_clear(GF_SURFACE _this, GF_IRect *rc, u32 color)
{
        GpPath *path;
        GPGRAPH();

        GdipCreatePath(FillModeAlternate, &path);
        if (rc) {
                /*luckily enough this maps well for both flipped and unflipped coords*/
                GdipAddPathRectangleI(path, rc->x, rc->y - rc->height, rc->width, rc->height);
        } else {
                /*              if (_graph->center_coords) {
                                        GdipAddPathRectangleI(path, -1 * (s32)_graph->w / 2, -1 * (s32)_graph->h / 2, _graph->w, _graph->h);
                                } else {
                */                      GdipAddPathRectangleI(path, 0, 0, _graph->w, _graph->h);
//              }
        }
        /*we MUST use clear otherwise ARGB surfaces are not cleared correctly*/
        GdipSetClipPath(_graph->graph, path, CombineModeReplace);
        GdipGraphicsClear(_graph->graph, color);
        GdipDeletePath(path);
        return GF_OK;
}

void gdip_init_driver_surface(GF_Raster2D *driver)
{
        driver->surface_new = gdip_new_surface;
        driver->surface_delete = gdip_delete_surface;
        driver->surface_attach_to_device = gdip_attach_surface_to_device;
        driver->surface_attach_to_texture = gdip_attach_surface_to_texture;
        driver->surface_attach_to_buffer = gdip_attach_surface_to_buffer;
        driver->surface_detach = gdip_detach_surface;
        driver->surface_set_raster_level = gdip_surface_set_raster_level;
        driver->surface_set_matrix = gdip_surface_set_matrix;
        driver->surface_set_clipper = gdip_surface_set_clipper;
        driver->surface_set_path = gdip_surface_set_path;
        driver->surface_fill = gdip_surface_fill;
        driver->surface_flush = gdip_surface_flush;
        driver->surface_clear = gdip_surface_clear;
}


GF_Raster2D *gdip_LoadRenderer()
{
        GdiplusStartupInput startupInput;
        GF_Raster2D *driver;
        struct _gdip_context *ctx;
        SAFEALLOC(ctx, struct _gdip_context);
        SAFEALLOC(driver, GF_Raster2D);
        GdiplusStartup(&ctx->gdiToken, &startupInput, NULL);
        driver->internal = ctx;
        GF_REGISTER_MODULE_INTERFACE(driver, GF_RASTER_2D_INTERFACE, "GDIplus 2D Raster", "gpac distribution")
        gdip_init_driver_texture(driver);
        gdip_init_driver_surface(driver);
        gdip_init_driver_grad(driver);
        return driver;
}

void gdip_ShutdownRenderer(GF_Raster2D *driver)
{
        struct _gdip_context *ctx = (struct _gdip_context *)driver->internal;

        GdiplusShutdown(ctx->gdiToken);
        gf_free(ctx);
        gf_free(driver);
}

/* [<][>][^][v][top][bottom][index][help] */