The Perl Advent Calendar needs more articles for 2022. Submit your idea today!
#include "win32\win32guts.h"
#ifndef _APRICOT_H_
#include "apricot.h"
#endif
#include "guts.h"
#include "Window.h"
#include "Icon.h"
#include "DeviceBitmap.h"

#ifdef __cplusplus
extern "C" {
#endif


#define  sys (( PDrawableData)(( PComponent) self)-> sysData)->
#define  dsys( view) (( PDrawableData)(( PComponent) view)-> sysData)->
#define var (( PWidget) self)->
#define HANDLE sys handle
#define DHANDLE(x) dsys(x) handle

static Bool
create_gdip_surface(Handle self)
{
	HRGN rgn;

	/* force null region as it lingers in GDI+ space somehow */
	rgn = CreateRectRgn(0,0,0,0);
	if ( GetClipRgn( sys ps, rgn ) > 0 )
		SelectClipRgn( sys ps, NULL);
	else
		rgn = NULL;

	GPCALL GdipCreateFromHDC(sys ps, &sys graphics);
	apiGPErrCheckRet(false);

	if ( rgn ) {
		SelectClipRgn( sys ps, rgn);
		GPCALL GdipSetClipHrgn(sys graphics, rgn, CombineModeReplace);
		apiGPErrCheck;
		DeleteObject( rgn);
	}

	return true;
}

#define CREATE_GDIP_SURFACE (( sys ps && sys graphics == NULL ) ? create_gdip_surface(self) : true)

Bool
apc_gp_set_alpha( Handle self, int alpha)
{
	if (
		( is_apt(aptDeviceBitmap) && ((PDeviceBitmap)self)->type == dbtBitmap) ||
		( is_apt(aptImage)        && ((PImage)self)-> type == imBW )
	)
		alpha = 255;

	sys alpha = alpha;

	if ( alpha < 255 ) {
		if ( !CREATE_GDIP_SURFACE) {
			sys alpha = 255;
			return false;
		}
	}

	if ( sys ps && sys graphics )
		stylus_change( self);

	if ( sys alphaArenaPalette ) {
		free(sys alphaArenaPalette);
		sys alphaArenaPalette = NULL;
	}

	return true;
}

Bool
apc_gp_set_antialias( Handle self, Bool aa)
{
	if ( aa ) {
		if (
			( is_apt(aptDeviceBitmap) && ((PDeviceBitmap)self)->type == dbtBitmap) ||
			( is_apt(aptImage)        && ((PImage)self)-> type == imBW )
		)
			return false;
		if ( !CREATE_GDIP_SURFACE)
			return false;
		apt_set(aptGDIPlus);
		GdipSetSmoothingMode(sys graphics, SmoothingModeAntiAlias);
		GdipSetPixelOffsetMode(sys graphics, PixelOffsetModeHalf);
	} else {
		apt_clear(aptGDIPlus);
		GdipSetSmoothingMode(sys graphics, SmoothingModeNone);
		GdipSetPixelOffsetMode(sys graphics, PixelOffsetModeNone);
	}
	return true;
}

int
apc_gp_get_alpha( Handle self)
{
	return sys alpha;
}

Bool
apc_gp_get_antialias( Handle self)
{
	return is_apt(aptGDIPlus);
}

Bool
apc_gp_aa_bar( Handle self, double x1, double y1, double x2, double y2)
{
	double dy = sys lastSize. y;
	Point t = sys gp_transform;
	objCheck false;

	x2 -= x1 - 1;
	y2 -= y1 - 1;
	x1 += t.x;
	y1 = t.y + dy - y1 - y2;

	STYLUS_USE_GP_BRUSH;
	GPCALL GdipFillRectangle(
		sys graphics,
		sys stylusGPResource-> brush,
		x1, y1, x2, y2
	);
	apiGPErrCheckRet(false);

	return true;
}

Bool
apc_gp_aa_fill_poly( Handle self, int numPts, NPoint * points)
{
	int i;
	double dy = sys lastSize. y;
	Point t = sys gp_transform;
	GpPointF *p;
	objCheck false;

	if ((p = malloc( sizeof(GpPointF) * numPts)) == NULL)
		return false;

	for ( i = 0; i < numPts; i++)  {
		p[i].X = t.x + points[i].x;
		p[i].Y = t.y + dy - points[i].y;
	}

	STYLUS_USE_GP_BRUSH;
	GPCALL GdipFillPolygon(
		sys graphics,
		sys stylusGPResource-> brush,
		p, numPts,
		((sys psFillMode & fmWinding) == fmAlternate) ?
			FillModeAlternate : FillModeWinding
	);
	apiGPErrCheckRet(false);

	return true;
}

/* emulate alpha text */

#define GRAD 57.29577951

void
aa_free_arena(Handle self, Bool for_reuse)
{
	if ( !for_reuse && sys alphaArenaPalette ) {
		free(sys alphaArenaPalette);
		sys alphaArenaPalette = NULL;
	}

	if ( !sys alphaArenaDC)
		return;

	if (sys alphaArenaStockFont)
		SelectObject( sys alphaArenaDC, sys alphaArenaStockFont);
	SelectObject( sys alphaArenaDC, sys alphaArenaStockBitmap);
	DeleteObject( sys alphaArenaBitmap );
	sys alphaArenaBitmap = NULL;
	sys alphaArenaStockFont = NULL;
	sys alphaArenaStockBitmap = NULL;
	if ( !for_reuse ) {
		DeleteDC(sys alphaArenaDC);
		sys alphaArenaDC = NULL;
	}

}

static Bool
aa_make_arena(Handle self)
{
	int w,h;
	w = var font. maximalWidth;
	h = var font. height;
	if ( w < h ) w = h;

	if ( var font. direction != 0) {
		if ( sys font_sin == sys font_cos && sys font_sin == 0.0 ) {
			sys font_sin = sin( var font. direction / GRAD);
			sys font_cos = cos( var font. direction / GRAD);
		}
	}

	if ( w < sys alphaArenaSize.x || h < sys alphaArenaSize.y || !sys alphaArenaDC ) {
		HDC dc;
		if ( sys alphaArenaBitmap ) {
			aa_free_arena(self, true);
		} else {
			if (!( sys alphaArenaDC = CreateCompatibleDC(0)))
				return false;
			SetTextAlign(sys alphaArenaDC, TA_BOTTOM);
			SetBkMode( sys alphaArenaDC, TRANSPARENT);
			SetTextColor( sys alphaArenaDC, 0xffffff);
		}

		sys alphaArenaSize.x = sys alphaArenaSize.y = w * 4;

		dc = dc_alloc();
		if ( !( sys alphaArenaBitmap = image_create_argb_dib_section(
			dc, sys alphaArenaSize.x, sys alphaArenaSize.y, &sys alphaArenaPtr
		))) {
			dc_free();
			return false;
		}
		dc_free();

		sys alphaArenaStockBitmap = SelectObject( sys alphaArenaDC, sys alphaArenaBitmap);
		sys alphaArenaStockFont = NULL;
		sys alphaArenaFontChanged = true;
	}

	if ( sys alphaArenaFontChanged || !sys alphaArenaStockFont) {
		HFONT f;
		f = SelectObject( sys alphaArenaDC, sys fontResource-> hfont );
		if ( !sys alphaArenaStockFont )
			sys alphaArenaStockFont = f;
		sys alphaArenaFontChanged = false;
	}

	return true;
}

static Bool
aa_render( Handle self, int x, int y, NPoint* delta, ABCFLOAT * abc, int advance, int dx, int dy)
{
	BLENDFUNCTION bf;
	float xx, yy, shift;
	register uint32_t * p, * palette;
	int i, j, miny, maxy, maxx, minx;
	Point sz;

	/* replace white to our color + alpha, calculate minimal affected box */
	p = sys alphaArenaPtr;
	palette = sys alphaArenaPalette;
	for (
		i = maxy = maxx = 0,
			minx = sys alphaArenaSize.x - 1,
			miny = sys alphaArenaSize.y - 1;
		i < sys alphaArenaSize.y;
		i++
	) {
		Bool match = false;
		for ( j = 0; j < sys alphaArenaSize.x; j++, p++) {
			if (1 || *p != 0) {
				register Byte *argb = (Byte*) p;
				*p = palette[argb[0] + argb[1] + argb[2]];
				if ( minx > j ) minx = j;
				if ( maxx < j ) maxx = j;
				match = true;
			}
		}
		if ( match ) {
			if ( miny > i ) miny = i;
			if ( maxy < i ) maxy = i;
		}
	}
	if ( maxy < miny ) return true;

	/* calculate advance for the next glyph and the position, if needed, for this one */
	if ( advance >= 0 ) {
		shift = advance;
		if ( var font.direction != 0 ) {
			xx = (dx * sys font_cos) - (dy * sys font_sin);
			yy = (dx * sys font_sin) + (dy * sys font_cos);
		} else {
			xx = dx;
			yy = dy;
		}
	} else {
		shift = abc->abcfA + abc->abcfB + abc->abcfC;
		xx = yy = 0.0;
	}
	x += round(delta->x + xx);
	y += round(delta->y - yy);

	if ( var font.direction != 0 ) {
		delta->x += shift * sys font_cos;
		delta->y -= shift * sys font_sin;
	} else {
		delta->x += shift;
	}

	/* calculate the aperture */
	sz = sys alphaArenaSize;
	x -= sz.x/2;
	y -= sz.y/2;
	if ( is_apt( aptTextOutBaseline)) {
		if ( var font. direction != 0 ) {
			float d = var font. descent;
			x += d * sys font_sin + .5;
			y += d * sys font_cos + .5;
		} else
			y += var font. descent;
	}

	/* minimize the blit rectangle */
	{
		int m, n;
		m = sys alphaArenaSize.y - maxy - 1;
		n = sys alphaArenaSize.y - miny - 1;
		miny = m;
		maxy = n;
	}
	sz.x = maxx - minx + 1;
	sz.y = maxy - miny + 1;

	/* blend */
	bf.BlendOp             = AC_SRC_OVER;
	bf.BlendFlags          = 0;
	bf.SourceConstantAlpha = 0xff;
	bf.AlphaFormat         = AC_SRC_ALPHA;
	return AlphaBlend(
		sys ps,           x + minx, y + miny,   sz.x, sz.y,
		sys alphaArenaDC, minx, miny,           sz.x, sz.y,
		bf);
}

/* precalculate alpha map */
Bool
aa_fill_palette(Handle self)
{
	int i,j,r,g,b;
	PStylus s = & sys stylus;

	if ( sys alphaArenaPalette )
		return true;

	if ( !( sys alphaArenaPalette = malloc(4 * 256 * 3)))
		return false;

	b = (s->pen.lopnColor >> 16) & 0xff;
	g = (s->pen.lopnColor & 0xff00) >> 8;
	r = s->pen.lopnColor & 0xff;

	for ( i = j = 0; i < 256; i++) {
		uint32_t a = sys alpha * i / 255;
		a =
			(a << 24)              |
			((r * a / 255 ) << 16) |
			((g * a / 255 ) << 8)  |
			( b * a / 255 )
			;
		sys alphaArenaPalette[j++] = a;
		sys alphaArenaPalette[j++] = a;
		sys alphaArenaPalette[j++] = a;
	}

	return true;
}

Bool
aa_text_out( Handle self, int x, int y, void * text, int len, Bool wide)
{
	int i;
	NPoint delta = { 0, 0 };

	if ( !(aa_fill_palette(self) && aa_make_arena(self)))
		return false;

	for ( i = 0; i < len; i++) {
		ABCFLOAT abc;
		memset(sys alphaArenaPtr, 0, sys alphaArenaSize.x * sys alphaArenaSize.y * 4);
		if ( wide ) {
			if (!GetCharABCWidthsFloatW( sys ps, ((WCHAR*)text)[i], ((WCHAR*)text)[i], &abc)) apiErrRet;
		} else {
			if (!GetCharABCWidthsFloatA( sys ps, ((char *)text)[i], ((char *)text)[i], &abc)) apiErrRet;
		}
		if ( wide ) {
			if (!TextOutW( sys alphaArenaDC, sys alphaArenaSize.x/2, sys alphaArenaSize.y/2, ((WCHAR*)text) + i, 1)) apiErrRet;
		} else {
			if (!TextOutA( sys alphaArenaDC, sys alphaArenaSize.x/2, sys alphaArenaSize.y/2, ((char *)text) + i, 1)) apiErrRet;
		}
		if ( !aa_render(self, x, y, &delta, &abc, -1, 0, 0))
			return false;
	}
	return true;
}

Bool
aa_glyphs_out( Handle self, PGlyphsOutRec t, int x, int y, int * text_advance, HFONT font)
{
	int i;
	NPoint delta = { 0, 0 };
	uint16_t *advances = t->advances;
	int16_t *positions = t->positions;
	HFONT f;

	if ( !(aa_fill_palette(self) && aa_make_arena(self)))
		return false;

	if ( text_advance )
		*text_advance = 0;

	f = SelectObject( sys alphaArenaDC, font );
	if ( !sys alphaArenaStockFont )
		sys alphaArenaStockFont = f;
	sys alphaArenaFontChanged = false;

	for ( i = 0; i < t->len; i++) {
		ABCFLOAT abc, *pabc;
		int adv, dx, dy;
		memset(sys alphaArenaPtr, 0, sys alphaArenaSize.x * sys alphaArenaSize.y * 4);
		if ( !ExtTextOutW(sys alphaArenaDC,
			sys alphaArenaSize.x/2, sys alphaArenaSize.y/2,
			ETO_GLYPH_INDEX, NULL, (LPCWSTR)(t->glyphs) + i, 1, 
			NULL
		))
			apiErrRet;

		if ( advances ) {
			adv = *(advances++);
			if ( text_advance )
				*text_advance += adv;
			pabc = NULL;
			dx = *(positions++);
			dy = *(positions++);
		} else {
			ABC abci;
			adv = -1;
			pabc = &abc;
			dx = dy = 0;
			if (!GetCharABCWidthsI( sys ps, ((WCHAR*)(t->glyphs))[i], 1, NULL, &abci)) apiErr;
			if ( text_advance )
				*text_advance += abci.abcA + abci.abcB + abci.abcC;
			abc.abcfA = abci.abcA;
			abc.abcfB = abci.abcB;
			abc.abcfC = abci.abcC;
		}
		if ( !aa_render(self, x, y, &delta, pabc, adv, dx, dy))
			return false;
	}

	return true;
}

#ifdef __cplusplus
}
#endif