1/*
2 * LegacyClonk
3 *
4 * Copyright (c) 1998-2000, Matthes Bender (RedWolf Design)
5 * Copyright (c) 2017-2022, The LegacyClonk Team and contributors
6 *
7 * Distributed under the terms of the ISC license; see accompanying file
8 * "COPYING" for details.
9 *
10 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11 * See accompanying file "TRADEMARK" for details.
12 *
13 * To redistribute this file separately, substitute the full license texts
14 * for the above references.
15 */
16
17/* A piece of a DirectDraw surface */
18
19#include <C4Include.h>
20#include <C4Facet.h>
21#include <C4Game.h>
22
23void C4Facet::Default()
24{
25 Set(nsfc: nullptr, nx: 0, ny: 0, nwdt: 0, nhgt: 0);
26}
27
28void C4Facet::Set(C4Surface *nsfc, int32_t nx, int32_t ny, int32_t nwdt, int32_t nhgt)
29{
30 Surface = nsfc; X = nx; Y = ny; Wdt = nwdt; Hgt = nhgt;
31}
32
33C4Facet::C4Facet()
34{
35 Default();
36}
37
38int32_t C4Facet::GetSectionCount()
39{
40 if (Hgt == 0) return 0;
41 return Wdt / Hgt;
42}
43
44C4Facet C4Facet::GetSection(int32_t iSection)
45{
46 C4Facet rval;
47 rval.Set(nsfc: Surface, nx: X + Hgt * iSection, ny: Y, nwdt: Hgt, nhgt: Hgt);
48 return rval;
49}
50
51void C4Facet::Draw(C4Surface *sfcTarget, int32_t iX, int32_t iY, int32_t iPhaseX, int32_t iPhaseY, const float scale)
52{
53 if (!lpDDraw || !Surface || !sfcTarget || !Wdt || !Hgt) return;
54
55 lpDDraw->Blit(sfcSource: Surface,
56 fx: float(X + Wdt * iPhaseX) * scale, fy: float(Y + Hgt * iPhaseY) * scale, fwdt: float(Wdt) * scale, fhgt: float(Hgt) * scale,
57 sfcTarget,
58 tx: iX, ty: iY, twdt: Wdt, thgt: Hgt, fSrcColKey: true);
59}
60
61void C4Facet::DrawT(C4Surface *sfcTarget, int32_t iX, int32_t iY, int32_t iPhaseX, int32_t iPhaseY, C4DrawTransform *pTransform, bool noScalingCorrection, const float scale)
62{
63 if (!lpDDraw || !Surface || !sfcTarget || !Wdt || !Hgt) return;
64
65 lpDDraw->Blit(sfcSource: Surface,
66 fx: float(X + Wdt * iPhaseX) * scale, fy: float(Y + Hgt * iPhaseY) * scale, fwdt: float(Wdt) * scale, fhgt: float(Hgt) * scale,
67 sfcTarget,
68 tx: iX, ty: iY, twdt: Wdt, thgt: Hgt, fSrcColKey: true, pTransform, noScalingCorrection);
69}
70
71void C4Facet::DrawT(C4Facet &cgo, bool fAspect, int32_t iPhaseX, int32_t iPhaseY, C4DrawTransform *pTransform, bool noScalingCorrection, const float scale)
72{
73 if (!lpDDraw || !Surface || !cgo.Surface || !Wdt || !Hgt) return;
74
75 // Drawing area
76 C4Facet ccgo = cgo;
77 // Adjust for fixed aspect ratio
78 if (fAspect)
79 {
80 // By height
81 if (100 * cgo.Wdt / Wdt < 100 * cgo.Hgt / Hgt)
82 {
83 ccgo.Hgt = Hgt * cgo.Wdt / Wdt;
84 ccgo.Y += (cgo.Hgt - ccgo.Hgt) / 2;
85 }
86 // By width
87 else if (100 * cgo.Hgt / Hgt < 100 * cgo.Wdt / Wdt)
88 {
89 ccgo.Wdt = Wdt * cgo.Hgt / Hgt;
90 ccgo.X += (cgo.Wdt - ccgo.Wdt) / 2;
91 }
92 }
93
94 lpDDraw->Blit(sfcSource: Surface,
95 fx: float(X + Wdt * iPhaseX) * scale, fy: float(Y + Hgt * iPhaseY) * scale, fwdt: float(Wdt) * scale, fhgt: float(Hgt) * scale,
96 sfcTarget: ccgo.Surface, tx: ccgo.X, ty: ccgo.Y, twdt: ccgo.Wdt, thgt: ccgo.Hgt,
97 fSrcColKey: true, pTransform, noScalingCorrection);
98}
99
100void C4Facet::Draw(C4Facet &cgo, bool fAspect, int32_t iPhaseX, int32_t iPhaseY, bool fTransparent, const float scale)
101{
102 // Valid parameter check
103 if (!lpDDraw || !Surface || !cgo.Surface || !Wdt || !Hgt) return;
104 // Drawing area
105 C4Facet ccgo = cgo;
106 // Adjust for fixed aspect ratio
107 if (fAspect)
108 {
109 // By height
110 if (100 * cgo.Wdt / Wdt < 100 * cgo.Hgt / Hgt)
111 {
112 ccgo.Hgt = Hgt * cgo.Wdt / Wdt;
113 ccgo.Y += (cgo.Hgt - ccgo.Hgt) / 2;
114 }
115 // By width
116 else if (100 * cgo.Hgt / Hgt < 100 * cgo.Wdt / Wdt)
117 {
118 ccgo.Wdt = Wdt * cgo.Hgt / Hgt;
119 ccgo.X += (cgo.Wdt - ccgo.Wdt) / 2;
120 }
121 }
122 // Blit
123 lpDDraw->Blit(sfcSource: Surface,
124 fx: float(X + Wdt * iPhaseX) * scale, fy: float(Y + Hgt * iPhaseY) * scale, fwdt: float(Wdt) * scale, fhgt: float(Hgt) * scale,
125 sfcTarget: ccgo.Surface,
126 tx: ccgo.X, ty: ccgo.Y, twdt: ccgo.Wdt, thgt: ccgo.Hgt,
127 fSrcColKey: fTransparent);
128}
129
130void C4Facet::DrawFullScreen(C4Facet &cgo)
131{
132 // stretched fullscreen blit: make sure right and lower side are cleared, because this may be missed due to stretching
133 if (cgo.Wdt > Wdt + 2 || cgo.Hgt > Wdt + 2)
134 {
135 lpDDraw->DrawBoxDw(sfcDest: cgo.Surface, iX1: cgo.X, iY1: cgo.Y + cgo.Hgt - 1, iX2: cgo.X + cgo.Wdt + 2, iY2: cgo.Y + cgo.Hgt + 2, dwClr: 0x00000000);
136 lpDDraw->DrawBoxDw(sfcDest: cgo.Surface, iX1: cgo.X + cgo.Wdt - 1, iY1: cgo.Y, iX2: cgo.X + cgo.Wdt + 2, iY2: cgo.Y + cgo.Hgt + 2, dwClr: 0x00000000);
137 }
138 // normal blit OK
139 Draw(cgo, fAspect: false);
140}
141
142void C4Facet::DrawClr(C4Facet &cgo, bool fAspect, uint32_t dwClr)
143{
144 if (!Surface) return;
145 // set ColorByOwner-color
146 Surface->SetClr(dwClr);
147 // draw
148 Draw(cgo, fAspect);
149}
150
151void C4Facet::DrawValue2Clr(C4Facet &cgo, int32_t iValue1, int32_t iValue2, uint32_t dwClr)
152{
153 // set ColorByOwner-color
154 Surface->SetClr(dwClr);
155 // draw
156 DrawValue2(cgo, iValue1, iValue2);
157}
158
159void C4Facet::DrawXR(C4Surface *sfcTarget, int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt, int32_t iSectionX, int32_t iSectionY, int32_t r)
160{
161 if (!lpDDraw || !Surface || !sfcTarget || !Wdt || !Hgt) return;
162 CBltTransform rot;
163 rot.SetRotate(iAngle: r, fOffX: (iX + iX + iWdt) / 2.f, fOffY: (iY + iY + iHgt) / 2.f);
164 lpDDraw->Blit(sfcSource: Surface,
165 fx: float(X + Wdt * iSectionX), fy: float(Y + Hgt * iSectionY), fwdt: float(Wdt), fhgt: float(Hgt),
166 sfcTarget,
167 tx: iX, ty: iY, twdt: iWdt, thgt: iHgt,
168 fSrcColKey: true, pTransform: &rot);
169}
170
171void C4Facet::DrawClrMod(C4Surface *sfcTarget, int32_t iX, int32_t iY, int32_t iPhaseX, int32_t iPhaseY, uint32_t dwModClr)
172{
173 // set color
174 if (!lpDDraw) return;
175 lpDDraw->ActivateBlitModulation(dwWithClr: dwModClr);
176 // blit
177 Draw(sfcTarget, iX, iY, iPhaseX, iPhaseY);
178 // reset color
179 lpDDraw->DeactivateBlitModulation();
180}
181
182C4Facet C4Facet::TruncateSection(int32_t iAlign)
183{
184 C4Facet fctResult; fctResult.Set(nsfc: Surface, nx: 0, ny: 0, nwdt: 0, nhgt: 0);
185 // Calculate section size
186 int32_t iWdt = Wdt, iHgt = Hgt;
187 switch (iAlign & C4FCT_Alignment)
188 {
189 case C4FCT_Left: case C4FCT_Right:
190 iWdt = Hgt;
191 if (iAlign & C4FCT_Triple) iWdt *= 3;
192 if (iAlign & C4FCT_Double) iWdt *= 2;
193 if (iAlign & C4FCT_Half) iWdt /= 2;
194 break;
195 case C4FCT_Top: case C4FCT_Bottom:
196 iHgt = Wdt;
197 if (iAlign & C4FCT_Triple) iHgt *= 3;
198 if (iAlign & C4FCT_Double) iHgt *= 2;
199 if (iAlign & C4FCT_Half) iHgt /= 2;
200 break;
201 }
202 // Size safety
203 if ((iWdt > Wdt) || (iHgt > Hgt)) return fctResult;
204 // Truncate
205 switch (iAlign & C4FCT_Alignment)
206 {
207 case C4FCT_Left: fctResult.Set(nsfc: Surface, nx: X, ny: Y, nwdt: iWdt, nhgt: iHgt); X += iWdt; Wdt -= iWdt; break;
208 case C4FCT_Right: fctResult.Set(nsfc: Surface, nx: X + Wdt - iWdt, ny: Y, nwdt: iWdt, nhgt: iHgt); Wdt -= iWdt; break;
209 case C4FCT_Top: fctResult.Set(nsfc: Surface, nx: X, ny: Y, nwdt: iWdt, nhgt: iHgt); Y += iHgt; Hgt -= iHgt; break;
210 case C4FCT_Bottom: fctResult.Set(nsfc: Surface, nx: X, ny: Y + Hgt - iHgt, nwdt: iWdt, nhgt: iHgt); Hgt -= iHgt; break;
211 }
212 // Done
213 return fctResult;
214}
215
216C4Facet C4Facet::Truncate(int32_t iAlign, int32_t iSize)
217{
218 C4Facet fctResult; fctResult.Set(nsfc: Surface, nx: 0, ny: 0, nwdt: 0, nhgt: 0);
219 // Calculate section size
220 int32_t iWdt = Wdt, iHgt = Hgt;
221 switch (iAlign)
222 {
223 case C4FCT_Left: case C4FCT_Right: iWdt = iSize; break;
224 case C4FCT_Top: case C4FCT_Bottom: iHgt = iSize; break;
225 }
226 // Size safety
227 if ((iWdt > Wdt) || (iHgt > Hgt)) return fctResult;
228 // Truncate
229 switch (iAlign)
230 {
231 case C4FCT_Left: fctResult.Set(nsfc: Surface, nx: X, ny: Y, nwdt: iWdt, nhgt: iHgt); X += iWdt; Wdt -= iWdt; break;
232 case C4FCT_Right: fctResult.Set(nsfc: Surface, nx: X + Wdt - iWdt, ny: Y, nwdt: iWdt, nhgt: iHgt); Wdt -= iWdt; break;
233 case C4FCT_Top: fctResult.Set(nsfc: Surface, nx: X, ny: Y, nwdt: iWdt, nhgt: iHgt); Y += iHgt; Hgt -= iHgt; break;
234 case C4FCT_Bottom: fctResult.Set(nsfc: Surface, nx: X, ny: Y + Hgt - iHgt, nwdt: iWdt, nhgt: iHgt); Hgt -= iHgt; break;
235 }
236 // Done
237 return fctResult;
238}
239
240void C4Facet::DrawValue(C4Facet &cgo, int32_t iValue, int32_t iSectionX, int32_t iSectionY, int32_t iAlign)
241{
242 if (!lpDDraw) return;
243 std::array<char, C4Strings::NumberOfCharactersForDigits<std::int32_t> + 1> buf;
244 *std::to_chars(first: buf.data(), last: buf.data() + buf.size() - 1, value: iValue).ptr = '\0';
245 switch (iAlign)
246 {
247 case C4FCT_Center:
248 Draw(cgo, fAspect: true, iPhaseX: iSectionX, iPhaseY: iSectionY);
249 lpDDraw->TextOut(szText: buf.data(), rFont&: Game.GraphicsResource.FontRegular, fZoom: 1.0, sfcDest: cgo.Surface,
250 iTx: cgo.X + cgo.Wdt - 1, iTy: cgo.Y + cgo.Hgt - 1, dwFCol: CStdDDraw::DEFAULT_MESSAGE_COLOR, byForm: ARight);
251 break;
252 case C4FCT_Right:
253 {
254 int32_t textwdt, texthgt;
255 Game.GraphicsResource.FontRegular.GetTextExtent(szText: buf.data(), rsx&: textwdt, rsy&: texthgt, fCheckMarkup: false);
256 lpDDraw->TextOut(szText: buf.data(), rFont&: Game.GraphicsResource.FontRegular, fZoom: 1.0, sfcDest: cgo.Surface,
257 iTx: cgo.X + cgo.Wdt - 1, iTy: cgo.Y, dwFCol: CStdDDraw::DEFAULT_MESSAGE_COLOR, byForm: ARight);
258 cgo.Set(nsfc: cgo.Surface, nx: cgo.X + cgo.Wdt - 1 - textwdt - 2 * cgo.Hgt, ny: cgo.Y, nwdt: 2 * cgo.Hgt, nhgt: cgo.Hgt);
259 Draw(cgo, fAspect: true, iPhaseX: iSectionX, iPhaseY: iSectionY);
260 break;
261 }
262 }
263}
264
265void C4Facet::DrawValue2(C4Facet &cgo, int32_t iValue1, int32_t iValue2, int32_t iSectionX, int32_t iSectionY, int32_t iAlign, int32_t *piUsedWidth)
266{
267 if (!lpDDraw) return;
268 std::array<char, C4Strings::NumberOfCharactersForDigits<std::int32_t> * 2 + 1 + 1> buf;
269
270 char *ptr{std::to_chars(first: buf.data(), last: buf.data() + C4Strings::NumberOfCharactersForDigits<std::int32_t>, value: iValue1).ptr};
271 *ptr++ = '/';
272 *std::to_chars(first: ptr, last: ptr + C4Strings::NumberOfCharactersForDigits<std::int32_t>, value: iValue2).ptr = '\0';
273
274 switch (iAlign)
275 {
276 case C4FCT_Center:
277 Draw(cgo, fAspect: true, iPhaseX: iSectionX, iPhaseY: iSectionY);
278 lpDDraw->TextOut(szText: buf.data(), rFont&: Game.GraphicsResource.FontRegular, fZoom: 1.0, sfcDest: cgo.Surface,
279 iTx: cgo.X + cgo.Wdt - 1, iTy: cgo.Y + cgo.Hgt - 1, dwFCol: CStdDDraw::DEFAULT_MESSAGE_COLOR, byForm: ARight);
280 break;
281 case C4FCT_Right:
282 {
283 int32_t textwdt, texthgt;
284 Game.GraphicsResource.FontRegular.GetTextExtent(szText: buf.data(), rsx&: textwdt, rsy&: texthgt, fCheckMarkup: false);
285 textwdt += Wdt + 3;
286 lpDDraw->TextOut(szText: buf.data(), rFont&: Game.GraphicsResource.FontRegular, fZoom: 1.0, sfcDest: cgo.Surface,
287 iTx: cgo.X + cgo.Wdt - 1, iTy: cgo.Y, dwFCol: CStdDDraw::DEFAULT_MESSAGE_COLOR, byForm: ARight);
288 cgo.Set(nsfc: cgo.Surface, nx: cgo.X + cgo.Wdt - textwdt, ny: cgo.Y, nwdt: 2 * cgo.Hgt, nhgt: cgo.Hgt);
289 Draw(cgo, fAspect: true, iPhaseX: iSectionX, iPhaseY: iSectionY);
290 if (piUsedWidth) *piUsedWidth = textwdt;
291 }
292 break;
293 }
294}
295
296void C4Facet::DrawX(C4Surface *sfcTarget, int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt, int32_t iSectionX, int32_t iSectionY, const float scale) const
297{
298 if (!lpDDraw || !Surface || !sfcTarget || !Wdt || !Hgt) return;
299 lpDDraw->Blit(sfcSource: Surface,
300 fx: float(X + Wdt * iSectionX) * scale, fy: float(Y + Hgt * iSectionY) * scale, fwdt: float(Wdt) * scale, fhgt: float(Hgt) * scale,
301 sfcTarget,
302 tx: iX, ty: iY, twdt: iWdt, thgt: iHgt,
303 fSrcColKey: true);
304}
305
306void C4Facet::DrawXFloat(C4Surface *sfcTarget, float fX, float fY, float fWdt, float fHgt) const
307{
308 if (!lpDDraw || !Surface || !sfcTarget || !Wdt || !Hgt || fWdt <= 0 || fHgt <= 0) return;
309 // Since only source coordinates are available as floats for blitting, go inwards into this facet to match blit
310 // for closest integer target coordinates
311 float zx = fWdt / float(Wdt), zy = fHgt / float(Hgt);
312 int32_t iX = static_cast<int32_t>(ceilf(x: fX)), iY = static_cast<int32_t>(ceilf(x: fY)), iX2 = static_cast<int32_t>(floorf(x: fX + fWdt)), iY2 = static_cast<int32_t>(floorf(x: fY + fHgt));
313 float ox = (-fX + iX) / zx, oy = (-fY + iY) / zy;
314 float oxs = (+fX + fWdt - iX2) / zx, oys = (+fY + fHgt - iY2) / zy;
315 lpDDraw->Blit(sfcSource: Surface,
316 fx: float(X) + ox, fy: float(Y) + oy, fwdt: float(Wdt) - ox - oxs, fhgt: float(Hgt) - oy - oys,
317 sfcTarget,
318 tx: iX, ty: iY, twdt: iX2 - iX, thgt: iY2 - iY,
319 fSrcColKey: true);
320 zx = (iX2 - iX) / (float(Wdt) - ox - oxs);
321 zy = (iY2 - iY) / (float(Hgt) - oy - oys);
322}
323
324void C4Facet::DrawXT(C4Surface *sfcTarget, int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt, int32_t iPhaseX, int32_t iPhaseY, C4DrawTransform *pTransform, bool noScalingCorrection, const float scale)
325{
326 if (!lpDDraw || !Surface || !sfcTarget || !Wdt || !Hgt) return;
327 lpDDraw->Blit(sfcSource: Surface,
328 fx: float(X + Wdt * iPhaseX) * scale, fy: float(Y + Hgt * iPhaseY) * scale, fwdt: float(Wdt) * scale, fhgt: float(Hgt) * scale,
329 sfcTarget,
330 tx: iX, ty: iY, twdt: iWdt, thgt: iHgt,
331 fSrcColKey: true, pTransform, noScalingCorrection);
332}
333
334void C4Facet::DrawEnergyLevelEx(int32_t iLevel, int32_t iRange, const C4Facet &gfx, int32_t bar_idx)
335{
336 // draw energy level using graphics
337 if (!lpDDraw || !gfx.Surface) return;
338 int32_t h = gfx.Hgt;
339 int32_t yBar = Hgt - BoundBy<int32_t>(bval: iLevel, lbound: 0, rbound: iRange) * Hgt / std::max<int32_t>(a: iRange, b: 1);
340 int32_t iY = 0, vidx = 0;
341 C4Facet gfx_draw = gfx;
342 bool filled = false;
343 while (iY < Hgt)
344 {
345 int32_t dy = iY % h;
346 int32_t dh = (iY >= Hgt - h) ? Hgt - iY : h - dy;
347 if (!filled)
348 {
349 if (iY >= yBar)
350 {
351 filled = true; // fully filled
352 }
353 else if (iY + h >= yBar)
354 dh = yBar - iY; // partially filled
355 }
356 if (!vidx && iY && iY + dh > h)
357 {
358 if (iY < h)
359 {
360 // had a break within top section of bar; finish top section
361 dh = h - iY;
362 }
363 else
364 {
365 // top section finished
366 ++vidx;
367 }
368 }
369 if (iY + dh >= Hgt - h)
370 {
371 if (iY >= Hgt - h)
372 {
373 // within bottom section
374 vidx = 2;
375 dy = iY + h - Hgt;
376 }
377 else
378 {
379 // finish middle section
380 dh = Hgt - h - iY;
381 }
382 }
383 // draw it; partially if necessary
384 gfx_draw.Y = gfx.Y + vidx * h + dy;
385 gfx_draw.Hgt = dh;
386 gfx_draw.Draw(sfcTarget: Surface, iX: X, iY: Y + iY, iPhaseX: bar_idx + bar_idx + !filled);
387 iY += dh;
388 }
389}
390
391bool C4Facet::GetPhaseNum(int32_t &rX, int32_t &rY)
392{
393 // safety
394 if (!Surface) return false;
395 // newgfx: use locally stored size
396 rX = Surface->Wdt / Wdt; rY = Surface->Hgt / Hgt;
397 // success
398 return true;
399}
400
401void C4DrawTransform::CompileFunc(StdCompiler *pComp)
402{
403 bool fCompiler = pComp->isCompiler();
404 int i;
405 // hacky. StdCompiler doesn't allow floats to be safed directly.
406 for (i = 0; i < 6; i++)
407 {
408 if (i) pComp->Separator();
409 std::string val; if (!fCompiler) val = fmt::sprintf(fmt: "%g", args: mat[i]);
410 pComp->Value(rStruct: mkParAdapt(rObj&: val, rPar: StdCompiler::RCT_Idtf));
411 if (fCompiler && pComp->hasNaming())
412 if (pComp->Separator(eSep: StdCompiler::SEP_PART))
413 {
414 std::string val2;
415 pComp->Value(rStruct: mkParAdapt(rObj&: val2, rPar: StdCompiler::RCT_Idtf));
416 val += '.'; val += val2;
417 }
418 if (fCompiler) sscanf(s: val.c_str(), format: "%g", &mat[i]);
419 }
420 pComp->Separator();
421 pComp->Value(rInt&: FlipDir);
422 if (!fCompiler && mat[6] == 0 && mat[7] == 0 && mat[8] == 1) return;
423 // because of backwards-compatibility, the last row comes after flipdir
424 for (i = 6; i < 9; ++i)
425 {
426 if (!pComp->Separator())
427 {
428 mat[i] = (i == 8) ? 1.0f : 0.0f;
429 }
430 else
431 {
432 std::string val; if (!fCompiler) val = fmt::sprintf(fmt: "%g", args: mat[i]);
433 pComp->Value(rStruct: mkParAdapt(rObj&: val, rPar: StdCompiler::RCT_Idtf));
434 if (fCompiler && pComp->hasNaming())
435 if (pComp->Separator(eSep: StdCompiler::SEP_PART))
436 {
437 std::string val2;
438 pComp->Value(rStruct: mkParAdapt(rObj&: val2, rPar: StdCompiler::RCT_Idtf));
439 val += '.'; val += val2;
440 }
441 if (fCompiler) sscanf(s: val.c_str(), format: "%g", &mat[i]);
442 }
443 }
444}
445
446void C4DrawTransform::SetTransformAt(C4DrawTransform &r, float iOffX, float iOffY)
447{
448 // Set matrix, so that this*(x,y,1)-(x,y,1)==this*(x+iOffX,y+iOffY,1)-(x+iOffX,y+iOffY,1)
449 float A = r.mat[0] + r.mat[6] * iOffX;
450 float B = r.mat[1] + r.mat[7] * iOffX;
451 float D = r.mat[3] + r.mat[6] * iOffY;
452 float E = r.mat[4] + r.mat[7] * iOffY;
453 CBltTransform::Set(
454 fA: A, fB: B, fC: r.mat[2] - A * iOffX - B * iOffY + r.mat[8] * iOffX,
455 fD: D, fE: E, fF: r.mat[5] - D * iOffX - E * iOffY + r.mat[8] * iOffY,
456 fG: r.mat[6], fH: r.mat[7], fI: r.mat[8] - r.mat[6] * iOffX - r.mat[7] * iOffY);
457}
458
459C4Facet C4Facet::GetFraction(int32_t percentWdt, int32_t percentHgt, int32_t alignX, int32_t alignY)
460{
461 C4Facet rval;
462 // Simple spec for square fractions
463 if (percentHgt == 0) percentHgt = percentWdt;
464 // Alignment
465 int iX = X, iY = Y, iWdt = (std::max)(a: Wdt * percentWdt / 100, b: 1), iHgt = (std::max)(a: Hgt * percentHgt / 100, b: 1);
466 if (alignX & C4FCT_Right) iX += Wdt - iWdt;
467 if (alignX & C4FCT_Center) iX += Wdt / 2 - iWdt / 2;
468 if (alignY & C4FCT_Bottom) iY += Hgt - iHgt;
469 if (alignY & C4FCT_Center) iY += Hgt / 2 - iHgt / 2;
470 // Set resulting facet
471 rval.Set(nsfc: Surface, nx: iX, ny: iY, nwdt: iWdt, nhgt: iHgt);
472 return rval;
473}
474