#include "dctx_win.inl"
#include "draw_win.inl"
#include "../draw.h"
#include "../draw.inl"
#include "../dctxh.h"
#include "../font.inl"
#include "../image.inl"
#include "../color.h"
#include "../font.h"
#include <geom2d/v2d.h>
#include <core/heap.h>
#include <sewer/bmath.h>
#include <sewer/cassert.h>
#include <sewer/unicode.h>
#if !defined(__WINDOWS__)
#error This file is only for Windows
#endif
static Gdiplus::ColorPalette *i_kGRAY4_PALETTE = NULL;
static Gdiplus::ColorPalette *i_kGRAY8_PALETTE = NULL;
int kLOG_PIXY = 0;
LONG kTWIPS_PER_PIXEL = 0;
void _draw_alloc_globals(void)
{
i_kGRAY4_PALETTE = NULL;
i_kGRAY8_PALETTE = NULL;
{
HWND hwnd = GetDesktopWindow();
HDC hdc = GetDC(hwnd);
kLOG_PIXY = GetDeviceCaps(hdc, LOGPIXELSY);
int ret = ReleaseDC(hwnd, hdc);
cassert_unref(ret == 1, ret);
kTWIPS_PER_PIXEL = 1440 / kLOG_PIXY;
}
}
void _draw_dealloc_globals(void)
{
cassert(i_kGRAY4_PALETTE == NULL);
if (i_kGRAY8_PALETTE != NULL)
heap_free(dcast(&i_kGRAY8_PALETTE, byte_t), sizeof(Gdiplus::ColorPalette) + 256 * sizeof(Gdiplus::ARGB), "Gray8Palette");
}
void _draw_word_extents(MeasureStr *data, const char_t *word, real32_t *width, real32_t *height)
{
SIZE word_size;
uint32_t num_chars = 0, num_bytes = 0;
WCHAR wword[256];
BOOL ret = 0;
cassert_no_null(data);
cassert_no_null(width);
cassert_no_null(height);
num_chars = unicode_nchars(word, ekUTF8);
num_bytes = unicode_convers(word, cast(wword, char_t), ekUTF8, ekUTF16, sizeof(wword));
cassert_unref(num_bytes < sizeof(wword), num_bytes);
ret = GetTextExtentPoint32(data->hdc, wword, (int)num_chars, &word_size);
cassert_unref(ret != 0, ret);
*width = (real32_t)word_size.cx;
*height = (real32_t)word_size.cy;
}
static ___INLINE void i_set_gdiplus_mode(DCtx *ctx)
{
cassert_no_null(ctx);
if (ctx->gdi_mode == TRUE)
{
ctx->gdi_mode = FALSE;
}
}
void _draw_imgimp(DCtx *ctx, const OSImage *image, const uint32_t frame_index, const real32_t x, const real32_t y, const bool_t raster)
{
Gdiplus::Bitmap *bitmap = NULL;
cassert_no_null(ctx);
cassert_no_null(ctx->graphics);
cassert_unref(raster == FALSE, raster);
bitmap = cast(osimage_native(image), Gdiplus::Bitmap);
i_set_gdiplus_mode(ctx);
if (frame_index != UINT32_MAX)
{
Gdiplus::Status status = bitmap->SelectActiveFrame(&Gdiplus::FrameDimensionTime, (UINT)frame_index);
cassert_unref(status == Gdiplus::Ok, status);
}
Gdiplus::REAL lx = (Gdiplus::REAL)x;
Gdiplus::REAL ly = (Gdiplus::REAL)y;
Gdiplus::REAL width = (Gdiplus::REAL)bitmap->GetWidth();
Gdiplus::REAL height = (Gdiplus::REAL)bitmap->GetHeight();
switch (ctx->image_halign)
{
case ekLEFT:
case ekJUSTIFY:
break;
case ekCENTER:
lx -= width / 2;
break;
case ekRIGHT:
lx -= width;
break;
}
switch (ctx->image_valign)
{
case ekTOP:
case ekJUSTIFY:
break;
case ekCENTER:
ly -= height / 2;
break;
case ekBOTTOM:
ly -= height;
break;
}
ctx->graphics->DrawImage(bitmap, lx, ly, width, height);
}
Gdiplus::ColorPalette *_dctx_8bpp_grayscale_palette(void)
{
if (i_kGRAY8_PALETTE == NULL)
{
uint32_t i = 0;
i_kGRAY8_PALETTE = cast(heap_malloc(sizeof(Gdiplus::ColorPalette) + 256 * sizeof(Gdiplus::ARGB), "Gray8Palette"), Gdiplus::ColorPalette);
i_kGRAY8_PALETTE->Flags = Gdiplus::PaletteFlagsGrayScale;
i_kGRAY8_PALETTE->Count = 256;
for (i = 0; i < 256; ++i)
i_kGRAY8_PALETTE->Entries[i] = Gdiplus::Color::MakeARGB(255, (BYTE)i, (BYTE)i, (BYTE)i);
}
return i_kGRAY8_PALETTE;
}
void draw_line(DCtx *ctx, const real32_t x0, const real32_t y0, const real32_t x1, const real32_t y1)
{
cassert_no_null(ctx);
cassert_no_null(ctx->graphics);
i_set_gdiplus_mode(ctx);
ctx->graphics->DrawLine(ctx->current_pen, (Gdiplus::REAL)x0, (Gdiplus::REAL)y0, (Gdiplus::REAL)x1, (Gdiplus::REAL)y1);
}
void draw_polyline(DCtx *ctx, bool_t closed, const V2Df *points, const uint32_t n)
{
cassert_no_null(ctx);
cassert_no_null(ctx->graphics);
cassert_no_null(points);
cassert(sizeof(V2Df) == sizeof(Gdiplus::PointF));
i_set_gdiplus_mode(ctx);
ctx->graphics->DrawLines(ctx->current_pen, (const Gdiplus::PointF *)points, (INT)n);
if (closed == TRUE)
ctx->graphics->DrawLine(ctx->current_pen, (Gdiplus::REAL)points[n - 1].x, (Gdiplus::REAL)points[n - 1].y, (Gdiplus::REAL)points[0].x, (Gdiplus::REAL)points[0].y);
}
void draw_arc(DCtx *ctx, const real32_t x, const real32_t y, const real32_t radius, const real32_t start, const real32_t sweep)
{
Gdiplus::RectF rect;
cassert_no_null(ctx);
cassert_no_null(ctx->graphics);
cassert(sizeof(V2Df) == sizeof(Gdiplus::PointF));
i_set_gdiplus_mode(ctx);
rect.X = (Gdiplus::REAL)(x - radius);
rect.Y = (Gdiplus::REAL)(y - radius);
rect.Width = (Gdiplus::REAL)(radius + radius);
rect.Height = rect.Width;
ctx->graphics->DrawArc(ctx->current_pen, rect, (Gdiplus::REAL)(start * kBMATH_RAD2DEGf), (Gdiplus::REAL)(sweep * kBMATH_RAD2DEGf));
}
void draw_bezier(DCtx *ctx, const real32_t x0, const real32_t y0, const real32_t x1, const real32_t y1, const real32_t x2, const real32_t y2, const real32_t x3, const real32_t y3)
{
cassert_no_null(ctx);
cassert_no_null(ctx->graphics);
i_set_gdiplus_mode(ctx);
ctx->graphics->DrawBezier(ctx->current_pen, (Gdiplus::REAL)x0, (Gdiplus::REAL)y0, (Gdiplus::REAL)x1, (Gdiplus::REAL)y1, (Gdiplus::REAL)x2, (Gdiplus::REAL)y2, (Gdiplus::REAL)x3, (Gdiplus::REAL)y3);
}
static Gdiplus::Color i_color(const color_t c)
{
uint8_t r, g, b, a;
color_get_rgba(c, &r, &g, &b, &a);
return Gdiplus::Color((BYTE)a, (BYTE)r, (BYTE)g, (BYTE)b);
}
static COLORREF i_colorref(const color_t color)
{
uint8_t r, g, b;
color_get_rgb(color, &r, &g, &b);
return RGB(r, g, b);
}
void draw_line_color(DCtx *ctx, const color_t color)
{
cassert_no_null(ctx);
if (ctx->line_color != color)
{
ctx->pen->SetColor(i_color(color));
ctx->current_pen = ctx->pen;
if (ctx->gdi_pen != NULL)
{
BOOL ok = DeleteObject(ctx->gdi_pen);
cassert_unref(ok != 0, ok);
ctx->gdi_pen = NULL;
}
ctx->gdi_pen = CreatePen(PS_SOLID, 1, i_colorref(color));
if (ctx->gdi_mode == TRUE)
SelectObject(ctx->hdc, ctx->gdi_pen);
ctx->line_color = color;
}
}
void draw_line_fill(DCtx *ctx)
{
cassert_no_null(ctx);
if (ctx->fpen == NULL)
{
Gdiplus::REAL pattern[16];
ctx->fpen = new Gdiplus::Pen((Gdiplus::ARGB)Gdiplus::Color::Black);
ctx->fpen->SetBrush(ctx->current_brush);
ctx->fpen->SetWidth(ctx->pen->GetWidth());
ctx->pen->GetDashPattern(pattern, ctx->pen->GetDashPatternCount());
ctx->fpen->SetDashPattern(pattern, ctx->pen->GetDashPatternCount());
ctx->fpen->SetDashStyle(ctx->pen->GetDashStyle());
}
ctx->current_pen = ctx->fpen;
}
void draw_line_width(DCtx *ctx, const real32_t width)
{
cassert_no_null(ctx);
ctx->pen->SetWidth((Gdiplus::REAL)width);
if (ctx->fpen != NULL)
ctx->fpen->SetWidth((Gdiplus::REAL)width);
}
static ___INLINE Gdiplus::LineCap i_linecap(const linecap_t cap)
{
switch (cap)
{
case ekLCFLAT:
return Gdiplus::LineCapFlat;
case ekLCSQUARE:
return Gdiplus::LineCapSquare;
case ekLCROUND:
return Gdiplus::LineCapRound;
cassert_default();
};
return Gdiplus::LineCapFlat;
}
void draw_line_cap(DCtx *ctx, const linecap_t cap)
{
cassert_no_null(ctx);
ctx->pen->SetLineCap(i_linecap(cap), i_linecap(cap), Gdiplus::DashCapFlat);
}
static ___INLINE Gdiplus::LineJoin i_linejoin(const linejoin_t join)
{
switch (join)
{
case ekLJMITER:
return Gdiplus::LineJoinMiter;
case ekLJROUND:
return Gdiplus::LineJoinRound;
case ekLJBEVEL:
return Gdiplus::LineJoinBevel;
cassert_default();
};
return Gdiplus::LineJoinMiter;
}
void draw_line_join(DCtx *ctx, const linejoin_t join)
{
cassert_no_null(ctx);
ctx->pen->SetLineJoin(i_linejoin(join));
}
void draw_line_dash(DCtx *ctx, const real32_t *pattern, const uint32_t n)
{
cassert_no_null(ctx);
if (pattern != NULL)
{
Gdiplus::Status status = ctx->pen->SetDashPattern(cast(pattern, Gdiplus::REAL), (INT)n);
ctx->pen->SetDashStyle(Gdiplus::DashStyleCustom);
cassert_unref(status == Gdiplus::Ok, status);
if (ctx->fpen != NULL)
{
ctx->fpen->SetDashPattern(cast(pattern, Gdiplus::REAL), (INT)n);
ctx->fpen->SetDashStyle(Gdiplus::DashStyleCustom);
}
}
else
{
ctx->pen->SetDashStyle(Gdiplus::DashStyleSolid);
if (ctx->fpen != NULL)
ctx->fpen->SetDashStyle(Gdiplus::DashStyleSolid);
}
}
static ___INLINE void i_draw_path(DCtx *ctx, Gdiplus::GraphicsPath *path, const drawop_t op)
{
cassert_no_null(path);
i_set_gdiplus_mode(ctx);
switch (op)
{
case ekSTROKE:
ctx->graphics->DrawPath(ctx->current_pen, path);
break;
case ekFILL:
ctx->graphics->FillPath(ctx->current_brush, path);
break;
case ekSKFILL:
ctx->graphics->DrawPath(ctx->current_pen, path);
ctx->graphics->FillPath(ctx->current_brush, path);
break;
case ekFILLSK:
ctx->graphics->FillPath(ctx->current_brush, path);
ctx->graphics->DrawPath(ctx->current_pen, path);
break;
cassert_default();
}
}
void draw_rect(DCtx *ctx, const drawop_t op, const real32_t x, const real32_t y, const real32_t width, const real32_t height)
{
Gdiplus::GraphicsPath path;
Gdiplus::REAL x0, x1, y0, y1;
cassert_no_null(ctx);
cassert_no_null(ctx->graphics);
x0 = (Gdiplus::REAL)x;
x1 = (Gdiplus::REAL)(x + width);
y0 = (Gdiplus::REAL)y;
y1 = (Gdiplus::REAL)(y + height);
path.AddLine(x0, y0, x1, y0);
path.AddLine(x1, y0, x1, y1);
path.AddLine(x1, y1, x0, y1);
path.CloseFigure();
i_draw_path(ctx, &path, op);
}
void draw_rndrect(DCtx *ctx, const drawop_t op, const real32_t x, const real32_t y, const real32_t width, const real32_t height, const real32_t radius)
{
Gdiplus::GraphicsPath path;
Gdiplus::REAL radi2 = radius * 2.f;
Gdiplus::REAL x1 = x + radius;
Gdiplus::REAL x2 = x + width - radi2;
Gdiplus::REAL x3 = x + width;
Gdiplus::REAL y1 = y + radius;
Gdiplus::REAL y2 = y + height - radi2;
Gdiplus::REAL y3 = y + height;
cassert_no_null(ctx);
cassert_no_null(ctx->graphics);
path.AddLine(x1, y, x2, y);
path.AddArc(x2, y, radi2, radi2, 270.f, 90.f);
path.AddLine(x3, y1, x3, y2);
path.AddArc(x2, y2, radi2, radi2, 0.f, 90.f);
path.AddLine(x2, y3, x1, y3);
path.AddArc(x, y2, radi2, radi2, 90.f, 90.f);
path.AddLine(x, y2, x, y1);
path.AddArc(x, y, radi2, radi2, 180.f, 90.f);
path.CloseFigure();
i_draw_path(ctx, &path, op);
}
void draw_circle(DCtx *ctx, const drawop_t op, const real32_t x, const real32_t y, const real32_t radius)
{
Gdiplus::GraphicsPath path;
Gdiplus::RectF rect;
cassert_no_null(ctx);
cassert_no_null(ctx->graphics);
rect.X = (Gdiplus::REAL)(x - radius);
rect.Y = (Gdiplus::REAL)(y - radius);
rect.Width = (Gdiplus::REAL)(radius + radius);
rect.Height = rect.Width;
path.AddEllipse(rect);
i_draw_path(ctx, &path, op);
}
void draw_ellipse(DCtx *ctx, const drawop_t op, const real32_t x, const real32_t y, const real32_t radx, const real32_t rady)
{
Gdiplus::GraphicsPath path;
Gdiplus::RectF rect;
cassert_no_null(ctx);
cassert_no_null(ctx->graphics);
rect.X = (Gdiplus::REAL)(x - radx);
rect.Y = (Gdiplus::REAL)(y - rady);
rect.Width = (Gdiplus::REAL)(radx + radx);
rect.Height = (Gdiplus::REAL)(rady + rady);
path.AddEllipse(rect);
i_draw_path(ctx, &path, op);
}
void draw_polygon(DCtx *ctx, const drawop_t op, const V2Df *points, const uint32_t n)
{
Gdiplus::GraphicsPath path;
cassert_no_null(ctx);
cassert_no_null(ctx->graphics);
cassert_no_null(points);
cassert(sizeof(V2Df) == sizeof(Gdiplus::PointF));
path.AddLines(cast(points, const Gdiplus::PointF), (INT)n);
path.CloseFigure();
i_draw_path(ctx, &path, op);
}
void draw_fill_color(DCtx *ctx, const color_t color)
{
cassert_no_null(ctx);
if (ctx->fill_color != color)
{
Gdiplus::Color c = i_color(color);
ctx->sbrush->SetColor(c);
if (ctx->gdi_sbrush != NULL)
{
BOOL ok = DeleteObject(ctx->gdi_sbrush);
cassert_unref(ok != 0, ok);
}
ctx->gdi_sbrush = CreateSolidBrush(c.ToCOLORREF());
ctx->fill_color = color;
}
ctx->current_brush = ctx->sbrush;
if (ctx->fpen != NULL)
ctx->fpen->SetBrush(ctx->current_brush);
}
static void i_set_gradient_colors(DCtx *ctx)
{
cassert_no_null(ctx);
if (ctx->gradient_wrap == Gdiplus::WrapModeClamp)
{
Gdiplus::REAL st = 10.f / 21.f;
Gdiplus::REAL norm = 1.f / 21.f;
Gdiplus::REAL stops[18];
cassert(ctx->gradient_n <= 16);
stops[0] = 0;
for (INT i = 0; i < ctx->gradient_n; ++i)
stops[i + 1] = st + (ctx->gradient_stops[i + 1] * norm);
stops[ctx->gradient_n + 1] = 1;
ctx->lbrush->SetWrapMode(Gdiplus::WrapModeTileFlipX);
ctx->lbrush->SetInterpolationColors(ctx->gradient_colors, stops, ctx->gradient_n + 2);
}
else
{
ctx->lbrush->SetWrapMode(ctx->gradient_wrap);
ctx->lbrush->SetInterpolationColors(ctx->gradient_colors + 1, ctx->gradient_stops + 1, ctx->gradient_n);
}
}
void draw_fill_linear(DCtx *ctx, const color_t *color, const real32_t *stop, const uint32_t n, const real32_t x0, const real32_t y0, const real32_t x1, const real32_t y1)
{
V2Df v;
uint32_t i;
cassert_no_null(ctx);
cassert(n < 16);
v.x = x1 - x0;
v.y = y1 - y0;
ctx->gradient_x = (Gdiplus::REAL)x0;
ctx->gradient_y = (Gdiplus::REAL)y0;
ctx->gradient_scale = (Gdiplus::REAL)v2d_lengthf(&v);
ctx->gradient_angle = (Gdiplus::REAL)bmath_atan2f(v.y, v.x);
ctx->gradient_angle *= kBMATH_RAD2DEGf;
ctx->gradient_colors[0] = i_color(color[0]);
ctx->gradient_stops[0] = 0;
for (i = 0; i < n; ++i)
{
ctx->gradient_colors[i + 1] = i_color(color[i]);
ctx->gradient_stops[i + 1] = (Gdiplus::REAL)stop[i];
}
ctx->gradient_colors[n + 1] = i_color(color[n - 1]);
ctx->gradient_stops[n + 1] = 1;
ctx->gradient_n = (INT)n;
i_set_gradient_colors(ctx);
ctx->current_brush = ctx->lbrush;
_dctx_gradient_transform(ctx);
if (ctx->fpen != NULL)
ctx->fpen->SetBrush(ctx->current_brush);
}
void draw_fill_matrix(DCtx *ctx, const T2Df *t2d)
{
cassert_no_null(ctx);
ctx->gradient_matrix->SetElements(
(Gdiplus::REAL)t2d->i.x,
(Gdiplus::REAL)t2d->i.y,
(Gdiplus::REAL)t2d->j.x,
(Gdiplus::REAL)t2d->j.y,
(Gdiplus::REAL)t2d->p.x,
(Gdiplus::REAL)t2d->p.y);
_dctx_gradient_transform(ctx);
if (ctx->fpen != NULL)
ctx->fpen->SetBrush(ctx->current_brush);
}
static ___INLINE Gdiplus::WrapMode i_wrap(const fillwrap_t wrap)
{
switch (wrap)
{
case ekFCLAMP:
return Gdiplus::WrapModeClamp;
case ekFTILE:
return Gdiplus::WrapModeTile;
case ekFFLIP:
return Gdiplus::WrapModeTileFlipX;
cassert_default();
}
return Gdiplus::WrapModeClamp;
}
void draw_fill_wrap(DCtx *ctx, const fillwrap_t wrap)
{
cassert_no_null(ctx);
ctx->gradient_wrap = i_wrap(wrap);
i_set_gradient_colors(ctx);
_dctx_gradient_transform(ctx);
if (ctx->fpen != NULL)
ctx->fpen->SetBrush(ctx->current_brush);
}
static void i_font(const Font *font, Gdiplus::Font **ffont, Gdiplus::FontFamily **ffamily, INT *fstyle, Gdiplus::REAL *fsize, Gdiplus::REAL *fintleading)
{
const char_t *family;
WCHAR wfamily[128];
uint32_t style;
INT lstyle;
family = font_family(font);
unicode_convers(family, cast(wfamily, char_t), ekUTF8, ekUTF16, sizeof(wfamily));
style = font_style(font);
lstyle = 0;
if (style & ekFBOLD)
lstyle |= Gdiplus::FontStyleBold;
if (style & ekFITALIC)
lstyle |= Gdiplus::FontStyleItalic;
if (style & ekFSTRIKEOUT)
lstyle |= Gdiplus::FontStyleStrikeout;
if (style & ekFUNDERLINE)
lstyle |= Gdiplus::FontStyleUnderline;
if (*ffamily != NULL)
delete *ffamily;
*ffamily = new Gdiplus::FontFamily(wfamily);
*fstyle = lstyle;
*fsize = (Gdiplus::REAL)font_size(font);
if (*ffont != NULL)
delete *ffont;
*ffont = new Gdiplus::Font(*ffamily, *fsize, *fstyle, (style & ekFPOINTS) == ekFPOINTS ? Gdiplus::UnitPoint : Gdiplus::UnitPixel);
*fintleading = 0;
}
void draw_font(DCtx *ctx, const Font *font)
{
cassert_no_null(ctx);
if (ctx->font == NULL)
{
ctx->font = font_copy(font);
i_font(ctx->font, &ctx->ffont, &ctx->ffamily, &ctx->fstyle, &ctx->fsize, &ctx->fintleading);
}
else if (font_equals(font, ctx->font) == FALSE)
{
font_destroy(&ctx->font);
ctx->font = font_copy(font);
i_font(ctx->font, &ctx->ffont, &ctx->ffamily, &ctx->fstyle, &ctx->fsize, &ctx->fintleading);
}
if (ctx->gdi_mode == TRUE)
{
cassert_no_null(ctx->hdc);
SelectObject(ctx->hdc, (HFONT)font_native(ctx->font));
}
}
static Gdiplus::StringAlignment i_align(const align_t align)
{
switch (align)
{
case ekLEFT:
case ekJUSTIFY:
return Gdiplus::StringAlignmentNear;
case ekCENTER:
return Gdiplus::StringAlignmentCenter;
case ekRIGHT:
return Gdiplus::StringAlignmentFar;
cassert_default();
}
return Gdiplus::StringAlignmentNear;
}
void draw_text_color(DCtx *ctx, const color_t color)
{
Gdiplus::Color c = i_color(color);
cassert_no_null(ctx);
ctx->text_color = color;
ctx->tbrush->SetColor(c);
SetTextColor(ctx->hdc, c.ToCOLORREF());
}
static Gdiplus::RectF i_text_origin(DCtx *ctx, const WCHAR *wtext, const real32_t x, const real32_t y)
{
Gdiplus::PointF origin;
Gdiplus::SizeF size;
cassert_no_null(ctx);
cassert_no_null(ctx->graphics);
origin.X = (Gdiplus::REAL)x;
origin.Y = (Gdiplus::REAL)y;
if (ctx->text_width > 0)
{
size.Width = ctx->text_width;
size.Height = 1000;
}
else
{
size.Width = 1e8f;
size.Height = 1e8f;
}
if (ctx->text_halign != ekLEFT || ctx->text_valign != ekTOP || ctx->text_intalign != ekLEFT)
{
Gdiplus::RectF layout;
Gdiplus::RectF out;
real32_t xscale = font_xscale(ctx->font);
layout.X = 0;
layout.Y = 0;
layout.Width = (Gdiplus::REAL)(ctx->text_width > 0 ? ctx->text_width / xscale : 1e8);
layout.Height = 1e8;
ctx->graphics->MeasureString(wtext, -1, ctx->ffont, layout, &out);
out.Width *= xscale;
if (ctx->text_width > 0 && ctx->text_ellipsis != ekELLIPMLINE)
{
Gdiplus::REAL fheight = font_height(ctx->font);
if (out.Height > 1.3 * fheight)
out.Height = fheight;
}
size.Width = out.Width;
size.Height = out.Height;
switch (ctx->text_halign)
{
case ekLEFT:
case ekJUSTIFY:
switch (ctx->text_intalign)
{
case ekLEFT:
case ekJUSTIFY:
break;
case ekCENTER:
if (ctx->text_width < 0)
origin.X += out.Width / 2;
break;
case ekRIGHT:
origin.X += out.Width;
break;
cassert_default();
}
break;
case ekCENTER:
switch (ctx->text_intalign)
{
case ekLEFT:
case ekJUSTIFY:
origin.X -= out.Width / 2;
break;
case ekCENTER:
if (ctx->text_width > 0)
origin.X -= out.Width / 2;
break;
case ekRIGHT:
origin.X += out.Width / 2;
break;
cassert_default();
}
break;
case ekRIGHT:
switch (ctx->text_intalign)
{
case ekLEFT:
case ekJUSTIFY:
origin.X -= out.Width;
break;
case ekCENTER:
if (ctx->text_width > 0)
origin.X -= out.Width;
else
origin.X -= out.Width / 2;
break;
case ekRIGHT:
break;
cassert_default();
}
break;
cassert_default();
}
switch (ctx->text_valign)
{
case ekTOP:
case ekJUSTIFY:
break;
case ekCENTER:
origin.Y -= out.Height / 2;
break;
case ekBOTTOM:
origin.Y -= out.Height;
break;
cassert_default();
}
}
return Gdiplus::RectF(origin, size);
}
void draw_text(DCtx *ctx, const char_t *text, const real32_t x, const real32_t y)
{
uint32_t num_chars = 0;
WCHAR *wtext = NULL;
WCHAR wtext_static[1024];
WCHAR *wtext_alloc = NULL;
Gdiplus::StringFormat format;
Gdiplus::RectF rect;
Gdiplus::Matrix matrix;
real32_t xscale = 1.f;
cassert_no_null(ctx);
cassert_no_null(ctx->graphics);
i_set_gdiplus_mode(ctx);
num_chars = 1 + unicode_nchars(text, ekUTF8);
if (num_chars < 1024)
{
wtext = wtext_static;
}
else
{
wtext_alloc = cast(heap_malloc(num_chars * sizeof(WCHAR), "OSDrawText"), WCHAR);
wtext = wtext_alloc;
}
{
uint32_t bytes = unicode_convers(text, cast(wtext, char_t), ekUTF8, ekUTF16, num_chars * sizeof(WCHAR));
cassert_unref(bytes == num_chars * sizeof(WCHAR), bytes);
}
rect = i_text_origin(ctx, wtext, x, y);
format.SetAlignment(i_align(ctx->text_intalign));
xscale = font_xscale(ctx->font);
if (bmath_absf(xscale - 1) > 0.01f)
{
ctx->graphics->GetTransform(&matrix);
ctx->graphics->TranslateTransform(rect.X, rect.Y);
ctx->graphics->ScaleTransform((Gdiplus::REAL)xscale, 1);
ctx->graphics->TranslateTransform(-rect.X, -rect.Y);
}
if (ctx->text_width < 0)
{
ctx->graphics->DrawString(wtext, -1, ctx->ffont, Gdiplus::PointF(rect.X, rect.Y), &format, ctx->tbrush);
}
else
{
Gdiplus::RectF erect = rect;
switch (ctx->text_ellipsis)
{
case ekELLIPNONE:
case ekELLIPBEGIN:
format.SetTrimming(Gdiplus::StringTrimmingCharacter);
break;
case ekELLIPMIDDLE:
format.SetTrimming(Gdiplus::StringTrimmingEllipsisPath);
break;
case ekELLIPEND:
format.SetTrimming(Gdiplus::StringTrimmingEllipsisCharacter);
break;
case ekELLIPMLINE:
format.SetTrimming(Gdiplus::StringTrimmingNone);
break;
cassert_default();
}
erect.Width /= xscale;
ctx->graphics->DrawString(wtext, -1, ctx->ffont, erect, &format, ctx->tbrush);
}
if (bmath_absf(xscale - 1) > 0.01f)
{
ctx->graphics->SetTransform(&matrix);
}
if (wtext_alloc != NULL)
heap_free(dcast(&wtext_alloc, byte_t), num_chars * sizeof(WCHAR), "OSDrawText");
}
void draw_text_single_line(DCtx *ctx, const char_t *text, const real32_t x, const real32_t y)
{
unref(ctx);
unref(text);
unref(x);
unref(y);
cassert(FALSE);
}
void draw_text_path(DCtx *ctx, const drawop_t op, const char_t *text, const real32_t x, const real32_t y)
{
uint32_t num_chars = 0;
WCHAR *wtext = NULL;
WCHAR wtext_static[1024];
WCHAR *wtext_alloc = NULL;
Gdiplus::StringFormat format;
Gdiplus::RectF rect;
Gdiplus::Matrix matrix;
real32_t xscale = 1.f;
cassert_no_null(ctx);
cassert_no_null(ctx->graphics);
i_set_gdiplus_mode(ctx);
num_chars = 1 + unicode_nchars(text, ekUTF8);
if (num_chars < 1024)
{
wtext = wtext_static;
}
else
{
wtext_alloc = cast(heap_malloc(num_chars * sizeof(WCHAR), "OSDrawText"), WCHAR);
wtext = wtext_alloc;
}
{
uint32_t bytes = unicode_convers(text, cast(wtext, char_t), ekUTF8, ekUTF16, num_chars * sizeof(WCHAR));
cassert_unref(bytes == num_chars * sizeof(WCHAR), bytes);
}
rect = i_text_origin(ctx, wtext, x, y);
format.SetAlignment(i_align(ctx->text_intalign));
format.SetLineAlignment(i_align(ctx->text_valign));
xscale = font_xscale(ctx->font);
if (bmath_absf(xscale - 1) > 0.01f)
{
ctx->graphics->GetTransform(&matrix);
ctx->graphics->TranslateTransform(rect.X, rect.Y);
ctx->graphics->ScaleTransform((Gdiplus::REAL)xscale, 1);
ctx->graphics->TranslateTransform(-rect.X, -rect.Y);
}
if (op == ekFILL && ctx->current_brush == ctx->sbrush)
{
if (ctx->text_width < 0)
{
ctx->graphics->DrawString(wtext, -1, ctx->ffont, Gdiplus::PointF(rect.X, rect.Y), &format, ctx->sbrush);
}
else
{
Gdiplus::RectF erect = rect;
erect.Width /= xscale;
ctx->graphics->DrawString(wtext, -1, ctx->ffont, erect, &format, ctx->sbrush);
}
}
else
{
Gdiplus::GraphicsPath path;
Gdiplus::REAL size = ctx->fsize - ctx->fintleading;
path.AddString(wtext, -1, ctx->ffamily, ctx->fstyle, size, Gdiplus::PointF(rect.X, rect.Y), &format);
i_draw_path(ctx, &path, op);
}
if (bmath_absf(xscale - 1) > 0.01f)
{
ctx->graphics->SetTransform(&matrix);
}
if (wtext_alloc != NULL)
heap_free(dcast(&wtext_alloc, byte_t), num_chars * sizeof(WCHAR), "OSDrawText");
}
void draw_text_width(DCtx *ctx, const real32_t width)
{
cassert_no_null(ctx);
ctx->text_width = width;
}
void draw_text_trim(DCtx *ctx, const ellipsis_t ellipsis)
{
cassert_no_null(ctx);
ctx->text_ellipsis = ellipsis;
}
static ___INLINE UINT i_gdi_halign(const align_t align)
{
switch (align)
{
case ekLEFT:
case ekJUSTIFY:
return TA_LEFT;
case ekCENTER:
return TA_CENTER;
case ekRIGHT:
return TA_RIGHT;
cassert_default();
}
return TA_LEFT;
}
static ___INLINE UINT i_gdi_valign(const align_t align)
{
switch (align)
{
case ekTOP:
case ekJUSTIFY:
return TA_TOP;
case ekCENTER:
return TA_BASELINE;
case ekBOTTOM:
return TA_BOTTOM;
cassert_default();
}
return TA_TOP;
}
void draw_text_align(DCtx *ctx, const align_t halign, const align_t valign)
{
cassert_no_null(ctx);
ctx->text_halign = halign;
ctx->text_valign = valign;
if (ctx->gdi_mode == TRUE)
{
UINT align = 0;
cassert_no_null(ctx);
cassert_no_null(ctx->hdc);
align |= i_gdi_halign(ctx->text_halign);
align |= i_gdi_valign(ctx->text_valign);
align |= TA_NOUPDATECP;
SetTextAlign(ctx->hdc, align);
}
}
void draw_text_halign(DCtx *ctx, const align_t halign)
{
cassert_no_null(ctx);
ctx->text_intalign = halign;
}
void draw_text_extents(DCtx *ctx, const char_t *text, const real32_t refwidth, real32_t *width, real32_t *height)
{
uint32_t num_chars = 0;
WCHAR *wtext = NULL;
WCHAR wtext_static[1024];
WCHAR *wtext_alloc = NULL;
real32_t xscale = 1.f;
Gdiplus::RectF layout;
Gdiplus::RectF out;
cassert_no_null(ctx);
cassert_no_null(ctx->graphics);
i_set_gdiplus_mode(ctx);
num_chars = 1 + unicode_nchars(text, ekUTF8);
if (num_chars < 1024)
{
wtext = wtext_static;
}
else
{
wtext_alloc = cast(heap_malloc(num_chars * sizeof(WCHAR), "OSDrawExtents"), WCHAR);
wtext = wtext_alloc;
}
{
uint32_t bytes = unicode_convers(text, cast(wtext, char_t), ekUTF8, ekUTF16, num_chars * sizeof(WCHAR));
cassert_unref(bytes == num_chars * sizeof(WCHAR), bytes);
}
xscale = font_xscale(ctx->font);
layout.X = 0;
layout.Y = 0;
layout.Width = (Gdiplus::REAL)(refwidth > 0 ? refwidth / xscale : 1e8);
layout.Height = 1e8;
ctx->graphics->MeasureString(wtext, -1, ctx->ffont, layout, &out);
*width = bmath_ceilf((real32_t)out.Width * xscale);
*height = bmath_ceilf((real32_t)out.Height);
if (wtext_alloc != NULL)
heap_free(dcast(&wtext_alloc, byte_t), num_chars * sizeof(WCHAR), "OSDrawExtents");
}
void draw_image_align(DCtx *ctx, const align_t halign, const align_t valign)
{
cassert_no_null(ctx);
ctx->image_halign = halign;
ctx->image_valign = valign;
}
static void i_set_gdi_mode(DCtx *ctx)
{
cassert_no_null(ctx);
if (ctx->gdi_mode == FALSE)
{
UINT align = 0;
cassert_no_null(ctx->hdc);
cassert_no_null(ctx->gdi_pen);
SetBkMode(ctx->hdc, TRANSPARENT);
SelectObject(ctx->hdc, (HFONT)font_native(ctx->font));
SelectObject(ctx->hdc, ctx->gdi_pen);
align |= i_gdi_halign(ctx->text_halign);
align |= i_gdi_valign(ctx->text_valign);
align |= TA_NOUPDATECP;
SetTextAlign(ctx->hdc, align);
ctx->gdi_mode = TRUE;
}
}
void draw_set_raster_mode(DCtx *ctx)
{
i_set_gdi_mode(ctx);
}
void draw_text_raster(DCtx *ctx, const char_t *text, const real32_t x, const real32_t y)
{
unref(ctx);
unref(text);
unref(x);
unref(y);
cassert(FALSE);
}
void draw_image_raster(DCtx *ctx, const Image *image, const uint32_t x, const uint32_t y)
{
unref(ctx);
unref(image);
unref(x);
unref(y);
cassert(FALSE);
}
void draw_line_imp(DCtx *ctx, const real32_t x0, const real32_t y0, const real32_t x1, const real32_t y1, const bool_t raster)
{
unref(ctx);
unref(x0);
unref(y0);
unref(x1);
unref(y1);
unref(raster);
cassert(FALSE);
}
void draw_rect_imp(DCtx *ctx, const drawop_t op, const real32_t x, const real32_t y, const real32_t width, const real32_t height, const bool_t raster)
{
unref(ctx);
unref(op);
unref(x);
unref(y);
unref(width);
unref(height);
unref(raster);
cassert(FALSE);
}