childs -> children.
[AROS.git] / workbench / libs / icon / layouticon.c
blobf3646e9bca79d2a72625a985fa79a8291b2c0980
1 /*
2 Copyright © 1995-2007, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 #include <aros/debug.h>
8 #include <exec/types.h>
9 #include <workbench/icon.h>
10 #include <utility/tagitem.h>
11 #include <graphics/gfxmacros.h>
12 #include <proto/icon.h>
14 #include "icon_intern.h"
15 #include "support_builtin.h"
17 /* Bitmap scaling */
18 static BOOL scaleToResolution(ULONG SrcWidth, ULONG SrcHeight,
19 UWORD SrcResX, UWORD SrcResY,
20 ULONG *DstWidth, ULONG *DstHeight,
21 UWORD DstResX, UWORD DstResY,
22 ULONG *ScaleXSrc, ULONG *ScaleYSrc,
23 ULONG *ScaleXDst, ULONG *ScaleYDst,
24 struct IconBase *IconBase);
26 static BOOL scaleToBounds(ULONG SrcWidth, ULONG SrcHeight,
27 UWORD MaxWidth, UWORD MaxHeight,
28 ULONG *DstWidth, ULONG *DstHeight,
29 ULONG *ScaleXSrc, ULONG *ScaleYSrc,
30 ULONG *ScaleXDst, ULONG *ScaleYDst,
31 struct IconBase *IconBase);
33 /* ARGB image scaling */
34 static void ScaleRect(ULONG *Target, const ULONG *Source, int SrcWidth, int SrcHeight, int TgtWidth, int TgtHeight);
36 /*****************************************************************************
38 NAME */
40 AROS_LH3(BOOL, LayoutIconA,
42 /* SYNOPSIS */
43 AROS_LHA(struct DiskObject *, icon, A0),
44 AROS_LHA(struct Screen *, screen, A1),
45 AROS_LHA(struct TagItem *, tags, A2),
47 /* LOCATION */
48 struct IconBase *, IconBase, 32, Icon)
50 /* FUNCTION
51 Adapt a palette-mapped icon for display.
53 INPUTS
55 RESULT
57 NOTES
58 Not implemented.
60 EXAMPLE
62 BUGS
64 SEE ALSO
66 INTERNALS
68 *****************************************************************************/
70 AROS_LIBFUNC_INIT
72 /* Default source DPI is the Amiga PAL DPI */
73 ULONG width, height;
74 ULONG scaleXsrc = 1, scaleYsrc = 1, scaleXdst = 1, scaleYdst = 1;
75 ULONG mutualexclude = (ULONG)icon->do_Gadget.MutualExclude;
76 ULONG scalebox;
77 struct NativeIcon *ni;
78 struct RastPort rp;
79 struct DrawInfo *dri;
80 struct ColorMap *cm;
81 BOOL ret;
82 int i, j;
83 const ULONG bflags = BMF_CLEAR;
84 ni = GetNativeIcon(icon, LB(IconBase));
85 if (!ni)
86 return TRUE;
88 if (ni->ni_Width <= 0)
89 ni->ni_Width = ni->ni_DiskObject.do_Gadget.Width;
90 if (ni->ni_Height <= 0)
91 ni->ni_Height = ni->ni_DiskObject.do_Gadget.Height;
93 D(bug("[%s] Icon %p, Screen %p, %xx%x icon\n", __func__, icon, screen, ni->ni_Width, ni->ni_Height));
95 /* Already mapped to this screen?
97 if (screen == ni->ni_Screen)
98 return TRUE;
100 for (i = 0; i < 2; i++) {
101 struct NativeIconImage *image = &ni->ni_Image[i];
103 if (image->BitMap) {
104 FreeBitMap(image->BitMap);
105 image->BitMap = NULL;
108 if (image->ARGBMap && image->ARGBMap != image->ARGB) {
109 FreeVec(image->ARGBMap);
112 if (image->Pen) {
113 for (j = 0; j < image->Pens; j++) {
114 ReleasePen(ni->ni_Screen->ViewPort.ColorMap, image->Pen[j]);
116 FreeVec(image->Pen);
117 image->Pen = NULL;
120 /* Remove the synthesized BitMask */
121 if (image->BitMask) {
122 FreeVec(image->BitMask);
123 image->BitMask = NULL;
127 ni->ni_Screen = NULL;
129 if (screen == NULL)
130 return TRUE;
132 dri = GetScreenDrawInfo(screen);
133 if (dri == NULL)
134 return FALSE;
136 /* Look up the DrawInfo Pens we will need for the
137 * layout of the border and frame.
139 for (i = 0; i < NUMDRIPENS; i++) {
140 if (i < dri->dri_NumPens)
141 ni->ni_Pens[i] = dri->dri_Pens[i];
142 else
143 ni->ni_Pens[i] = dri->dri_Pens[DETAILPEN];
146 cm = screen->ViewPort.ColorMap;
148 /* NOTE: The ARGB data (if present) will not need
149 * any layout work.
151 D(bug("%s: Screen %p, Depth %d, ColorMap %d\n", __func__, screen, dri->dri_Depth, cm ? cm->Count : -1));
153 ret = TRUE;
155 /* Calculate the scaling factors
157 if (ni->ni_Face.Width && ni->ni_Face.Height) {
158 width = ni->ni_Face.Width;
159 height= ni->ni_Face.Height;
160 } else {
161 width = icon->do_Gadget.Width;
162 height = icon->do_Gadget.Height;
165 if (ni->ni_ScaleBox == ICON_SCALEBOX_DEFAULT)
166 scalebox = LB(IconBase)->ib_ScaleBox;
167 else
168 scalebox = ni->ni_ScaleBox;
170 ni->ni_Width = width;
171 ni->ni_Height = height;
173 /* Are we rescaling dynamically? */
174 if (scalebox == ICON_SCALEBOX_AUTOSCALE) {
175 UBYTE tpdX = 0, tpdY = 0;
177 /* Check for a magic MutualExlcude value
178 * that encodes Tick-Per-Dot information.
179 * MutalExclude of 0xffffffff is not valid.
181 if ((mutualexclude != 0xffffffff) && (mutualexclude & (1 << 31))) {
182 /* tpd information is in the lower 16 bits */
183 tpdX = (mutualexclude >> 8) & 0xff;
184 tpdY = (mutualexclude >> 0) & 0xff;
187 if (tpdX && tpdY) {
188 scaleToResolution(width, height, tpdX, tpdY,
189 &ni->ni_Width, &ni->ni_Height, dri->dri_Resolution.X, dri->dri_Resolution.Y,
190 &scaleXsrc, &scaleYsrc, &scaleXdst, &scaleYdst,
191 IconBase);
193 D(bug("%s: Icon tpd (%d:%d), Screen tpd (%d:%d)\n", __func__,
194 tpdX, tpdY, dri->dri_Resolution.X, dri->dri_Resolution.Y));
196 } else {
197 WORD MaxWidth, MaxHeight;
199 UNPACK_ICON_SCALEBOX(scalebox, MaxWidth, MaxHeight);
201 scaleToBounds(width, height, MaxWidth, MaxHeight,
202 &ni->ni_Width, &ni->ni_Height,
203 &scaleXsrc, &scaleYsrc, &scaleXdst, &scaleYdst,
204 IconBase);
207 for (i = 0; i < 2; i++) {
208 struct NativeIconImage *image = &ni->ni_Image[i];
209 struct TagItem pentags[] = {
210 { OBP_Precision, IconBase->ib_Precision },
211 { OBP_FailIfBad, FALSE },
212 { TAG_MORE, (IPTR)tags },
214 UBYTE *idata;
215 ULONG x;
216 UWORD bmdepth;
218 bmdepth = GetBitMapAttr(screen->RastPort.BitMap, BMA_DEPTH);
220 /* If we can use ARGB data, then use it! */
221 D(bug("[%s] Screen depth is %d\n", __func__, bmdepth));
222 if ((bmdepth > 8) && CyberGfxBase) {
223 FetchIconARGB(icon, i);
224 if (image->ARGB) {
225 if (width != ni->ni_Width || height != ni->ni_Height) {
226 if ((image->ARGBMap = AllocVec(ni->ni_Width * ni->ni_Height * sizeof(ULONG), MEMF_PUBLIC))) {
227 D(bug("[%s] ARGB scaling\n"));
228 ScaleRect(image->ARGBMap, image->ARGB, width, height, ni->ni_Width, ni->ni_Height);
229 width = ni->ni_Width;
230 height = ni->ni_Height;
232 } else {
233 image->ARGBMap = (APTR)image->ARGB;
236 if (image->ARGBMap) {
237 continue;
238 } else {
239 D(bug("[%s] No ARGB image\n"));
243 /* Allocate a bitmap, which is a 'friend' of the screen */
244 image->BitMap = AllocBitMap(width, height, dri->dri_Depth, bflags, screen->RastPort.BitMap);
245 if (image->BitMap == NULL) {
246 SetIoErr(ERROR_NO_FREE_STORE);
247 ret = FALSE;
248 goto exit;
251 FetchIconImage(icon, i);
253 if (!image->ImageData) {
254 struct Image *gi = NULL;
255 ULONG state;
256 BOOL flood = FALSE;
258 if (i == 1 && (icon->do_Gadget.Flags & GFLG_GADGHIMAGE)) {
259 gi = icon->do_Gadget.SelectRender;
260 } else if (icon->do_Gadget.Flags & GFLG_GADGIMAGE) {
261 gi = icon->do_Gadget.GadgetRender;
264 if (gi == NULL)
265 continue;
267 if (i == 1 && (icon->do_Gadget.Flags & GFLG_GADGHCOMP))
268 state = IDS_SELECTED;
269 else
270 state = IDS_NORMAL;
272 if (i == 1 && (icon->do_Gadget.Flags & GFLG_GADGBACKFILL)) {
273 state = IDS_SELECTED;
274 flood = TRUE;
277 InitRastPort(&rp);
278 if (!flood) {
279 rp.BitMap = image->BitMap;
280 DrawImageState(&rp, gi, 0, 0, state, dri);
281 } else {
282 /* Create a bitmap with a 1 pixel border,
283 * fill it with the inverse color,
284 * draw the inverse image into it,
285 * then flood-fill the border with color 0.
287 * Finally, copy the final image to the
288 * destination bitmap.
290 struct BitMap *bm;
291 if ((bm = AllocBitMap(gi->Width+2, gi->Height+2, gi->Depth, BMF_CLEAR, NULL))) {
292 PLANEPTR trbuf;
294 rp.BitMap = bm;
296 if ((trbuf = AllocRaster(gi->Width+2, gi->Height+2))) {
297 struct TmpRas tr;
298 InitTmpRas(&tr, trbuf, RASSIZE(gi->Width+2, gi->Height+2));
299 rp.TmpRas = &tr;
300 SetAPen(&rp, (1 << gi->Depth)-1);
301 RectFill(&rp, 0, 0, gi->Width+1, gi->Height+1);
302 DrawImageState(&rp, gi, 1, 1, state, dri);
303 SetAPen(&rp, 0);
304 Flood(&rp, 1, 0, 0);
305 BltBitMap(bm, 1, 1, image->BitMap, 0, 0, gi->Width, gi->Height, 0xc0, ~0, NULL);
306 FreeRaster(trbuf, gi->Width+2, gi->Height+2);
308 FreeBitMap(bm);
312 goto rescale;
315 /* Palettized image processing */
316 if (!image->Pen)
317 image->Pen = AllocVec(image->Pens * sizeof(image->Pen[0]), MEMF_PUBLIC | MEMF_CLEAR);
319 if (!image->Pen) {
320 SetIoErr(ERROR_NO_FREE_STORE);
321 ret = FALSE;
322 goto exit;
325 /* Get the needed colormap entries. */
326 for (j = 0; j < image->Pens; j++) {
327 ULONG r,g,b;
328 LONG pen;
329 /* CHECKME: So, uh, how does one accuarately
330 * convert 8 bit RGB to 32 bit RBG?
332 r = image->Palette[j].red << 24;
333 g = image->Palette[j].green << 24;
334 b = image->Palette[j].blue << 24;
335 pen = ObtainBestPenA(cm, r, g, b, pentags);
336 image->Pen[j] = (UBYTE)pen;
339 /* Draw the selected state into the screen's pens
341 * We take the risk of yet another memory allocation
342 * so that we can use WriteChunkyPixels(), which is
343 * GOBS faster than WritePixel().
345 idata = AllocVec(height * width, MEMF_ANY);
346 if (idata == NULL) {
347 FreeBitMap(image->BitMap);
348 image->BitMap = NULL;
349 SetIoErr(ERROR_NO_FREE_STORE);
350 ret = FALSE;
351 goto exit;
353 CopyMem(image->ImageData, idata, height * width);
354 for (x = 0; x < (height * width); x++) {
355 idata[x] = image->Pen[image->ImageData[x]];
357 InitRastPort(&rp);
358 rp.BitMap = image->BitMap;
359 WriteChunkyPixels(&rp, 0, 0, width - 1, height - 1,
360 idata, width);
361 FreeVec(idata);
363 /* Synthesize a bitmask for transparentcolor icons */
364 D(bug("[%s] TransparentColor %d\n", __func__, image->TransparentColor));
365 if (image->TransparentColor >= 0) {
366 int x, y;
367 UBYTE *row;
368 CONST UBYTE *img;
369 UWORD bpr = image->BitMap->BytesPerRow;
370 image->BitMask = AllocVec(bpr * height + 4, MEMF_PUBLIC | MEMF_CHIP | MEMF_CLEAR);
371 if (!image->BitMask) {
372 SetIoErr(ERROR_NO_FREE_STORE);
373 return FALSE;
376 img = image->ImageData;
377 row = image->BitMask;
378 #ifdef __mc68000 /* AGA support */
379 row = (APTR)(((IPTR)row + 7) & ~7);
380 #endif
381 for (y = 0; y < height; y++, row += bpr) {
382 for (x = 0; x < width; x++, img++) {
383 if ((*img != image->TransparentColor)) {
384 row[x>>3] |= 1 << (7 - (x & 7));
390 rescale:
391 if (width != ni->ni_Width || height != ni->ni_Height) {
392 struct BitMap *bm = AllocBitMap(ni->ni_Width, ni->ni_Height, dri->dri_Depth, bflags, image->BitMap);
394 D(bug("%s: Rescaling from %dx%d to %dx%d\n", __func__,
395 width, height, ni->ni_Width, ni->ni_Height));
397 if (bm) {
398 struct BitScaleArgs bsa = {
399 .bsa_SrcBitMap = image->BitMap,
400 .bsa_SrcX = 0,
401 .bsa_SrcY = 0,
402 .bsa_SrcWidth = width,
403 .bsa_SrcHeight = height,
404 .bsa_XSrcFactor = scaleXsrc,
405 .bsa_XDestFactor = scaleXdst,
406 .bsa_YSrcFactor = scaleYsrc,
407 .bsa_YDestFactor = scaleYdst,
408 .bsa_DestBitMap = bm,
410 BitMapScale(&bsa);
412 if (image->BitMask) {
413 struct BitMap src, dst;
414 PLANEPTR dst_mask;
415 src.BytesPerRow = image->BitMap->BytesPerRow;
416 src.Rows = height;
417 src.Flags = 0;
418 src.Depth = 1;
419 src.Planes[0] = image->BitMask;
420 #ifdef __mc68000 /* AGA support */
421 src.Planes[0] = (APTR)(((IPTR)src.Planes[0] + 7) & ~7);
422 #endif
423 dst.BytesPerRow = bm->BytesPerRow;
424 dst.Rows = ni->ni_Height;
425 dst.Flags = 0;
426 dst.Depth = 1;
427 dst_mask = AllocVec(dst.BytesPerRow * dst.Rows + 4, MEMF_PUBLIC | MEMF_CHIP | MEMF_CLEAR);
428 dst.Planes[0] = dst_mask;
429 if (dst.Planes[0]) {
430 #ifdef __mc68000 /* AGA support */
431 dst.Planes[0] = (APTR)(((IPTR)dst.Planes[0] + 7) & ~7);
432 #endif
433 bsa.bsa_SrcBitMap = &src;
434 bsa.bsa_DestBitMap = &dst;
435 bsa.bsa_SrcX = 0,
436 bsa.bsa_SrcY = 0,
437 bsa.bsa_SrcWidth = width,
438 bsa.bsa_SrcHeight = height,
439 bsa.bsa_XSrcFactor = scaleXsrc,
440 bsa.bsa_XDestFactor = scaleXdst,
441 bsa.bsa_YSrcFactor = scaleYsrc,
442 bsa.bsa_YDestFactor = scaleYdst,
444 BitMapScale(&bsa);
445 FreeVec(image->BitMask);
446 image->BitMask = dst_mask;
447 } else {
448 FreeVec(dst_mask);
449 FreeBitMap(bm);
450 continue;
454 FreeBitMap(image->BitMap);
455 image->BitMap = bm;
460 /* Hack to support Directory Opus 4/5, which insists
461 * on drawing its own icon imagery instead of calling
462 * DrawIconState().
464 if (!icon->do_Gadget.GadgetRender) {
465 GetBuiltinImage(&ni->ni_Image[0].Render, icon->do_Type, FALSE);
466 icon->do_Gadget.GadgetRender = &ni->ni_Image[0].Render;
469 if (!icon->do_Gadget.SelectRender) {
470 GetBuiltinImage(&ni->ni_Image[1].Render, icon->do_Type, TRUE);
471 icon->do_Gadget.SelectRender = &ni->ni_Image[1].Render;
474 ni->ni_Screen = screen;
476 exit:
477 FreeScreenDrawInfo(screen, dri);
479 if (ret == TRUE)
480 SetIoErr(0);
482 return ret;
484 AROS_LIBFUNC_EXIT
485 } /* LayoutIconA() */
488 static BOOL scaleToBounds(ULONG SrcWidth, ULONG SrcHeight,
489 UWORD MaxWidth, UWORD MaxHeight,
490 ULONG *DstWidth, ULONG *DstHeight,
491 ULONG *ScaleXSrc, ULONG *ScaleYSrc,
492 ULONG *ScaleXDst, ULONG *ScaleYDst,
493 struct IconBase *IconBase)
495 ULONG scaleXsrc, scaleYsrc, scaleXdst, scaleYdst;
497 if (MaxWidth <= 0 || MaxHeight <= 0)
498 return FALSE;
500 /* Scaling calculations
502 scaleXsrc = SrcWidth;
503 scaleYsrc = SrcHeight;
504 scaleXdst = MaxWidth;
505 scaleYdst = SrcHeight * MaxWidth / SrcWidth;
507 if (scaleYdst > MaxHeight) {
508 LONG delta = scaleYdst - MaxHeight;
509 scaleXdst -= delta * SrcWidth / SrcHeight;
510 scaleYdst -= delta;
513 while (scaleXsrc > 168383 || scaleXdst > 168383) {
514 scaleXsrc >>= 1;
515 scaleXdst >>= 1;
516 if (scaleXsrc == 0 || scaleXdst == 0) {
517 D(bug("\tCan't scale X from %dx%d to %dx%d\n", scaleXsrc, scaleYsrc, scaleXdst, scaleYdst));
518 return FALSE;
522 while (scaleYsrc > 168383 || scaleYdst > 168383) {
523 scaleYsrc >>= 1;
524 scaleYdst >>= 1;
525 if (scaleYsrc == 0 || scaleYdst == 0) {
526 D(bug("\tCan't scale Y from %dx%d to %dx%d\n", scaleXsrc, scaleYsrc, scaleXdst, scaleYdst));
527 return FALSE;
531 *DstWidth = ScalerDiv(SrcWidth, scaleXdst, scaleXsrc);
532 *DstHeight = ScalerDiv(SrcHeight, scaleYdst, scaleYsrc);
534 *ScaleXSrc = scaleXsrc;
535 *ScaleYSrc = scaleYsrc;
537 *ScaleXDst = scaleXdst;
538 *ScaleYDst = scaleYdst;
540 D(bug("[%s] Scale icon %dx%d to box %dx%d => %dx%d\n", __func__, SrcWidth, SrcHeight, MaxWidth, MaxHeight, *DstWidth, *DstHeight));
542 return TRUE;
545 static BOOL scaleToResolution(ULONG SrcWidth, ULONG SrcHeight,
546 UWORD SrcResX, UWORD SrcResY,
547 ULONG *DstWidth, ULONG *DstHeight,
548 UWORD DstResX, UWORD DstResY,
549 ULONG *ScaleXSrc, ULONG *ScaleYSrc,
550 ULONG *ScaleXDst, ULONG *ScaleYDst,
551 struct IconBase *IconBase)
553 ULONG scaleXsrc, scaleYsrc, scaleXdst, scaleYdst;
555 /* Scaling calculations
556 * Remember: 'res' is in 'ticks', which is inversely
557 * related to display DPI.
559 scaleXsrc = SrcWidth;
560 scaleYsrc = SrcHeight;
561 scaleXdst = SrcWidth * SrcResX / DstResX;
562 scaleYdst = SrcHeight * SrcResY / DstResY;
564 while (scaleXsrc > 168383 || scaleXdst > 168383) {
565 scaleXsrc >>= 1;
566 scaleXdst >>= 1;
567 if (scaleXsrc == 0 || scaleXdst == 0) {
568 D(bug("\tCan't scale X from %dx%d to %dx%d\n", scaleXsrc, scaleYsrc, scaleXdst, scaleYdst));
569 return FALSE;
573 while (scaleYsrc > 168383 || scaleYdst > 168383) {
574 scaleYsrc >>= 1;
575 scaleYdst >>= 1;
576 if (scaleYsrc == 0 || scaleYdst == 0) {
577 D(bug("\tCan't scale Y from %dx%d to %dx%d\n", scaleXsrc, scaleYsrc, scaleXdst, scaleYdst));
578 return FALSE;
582 *DstWidth = ScalerDiv(SrcWidth, scaleXdst, scaleXsrc);
583 *DstHeight = ScalerDiv(SrcHeight, scaleYdst, scaleYsrc);
585 *ScaleXSrc = scaleXsrc;
586 *ScaleYSrc = scaleYsrc;
588 *ScaleXDst = scaleXdst;
589 *ScaleYDst = scaleYdst;
591 D(bug("[%s] Scale icon %dx%d => %dx%d\n", __func__, SrcWidth, SrcHeight, *DstWidth, *DstHeight));
593 return TRUE;
596 /* From 'Image Scaling With Bresenham', Dr. Dobbs Journal, May 1, 2002
598 static inline void ScaleLine(ULONG *Target, const ULONG *Source, int SrcWidth, int TgtWidth)
600 int NumPixels = TgtWidth;
601 int IntPart = SrcWidth / TgtWidth;
602 int FracPart = SrcWidth % TgtWidth;
603 int E = 0;
604 while (NumPixels-- > 0) {
605 *(Target++) = *Source;
606 Source += IntPart;
607 E += FracPart;
608 if (E >= TgtWidth) {
609 E -= TgtWidth;
610 Source++;
615 static void ScaleRect(ULONG *Target, const ULONG *Source, int SrcWidth, int SrcHeight, int TgtWidth, int TgtHeight)
617 int NumPixels = TgtHeight;
618 int IntPart = (SrcHeight / TgtHeight) * SrcWidth;
619 int FractPart = SrcHeight % TgtHeight;
620 int E = 0;
621 const ULONG *PrevSource = NULL;
622 while (NumPixels-- > 0) {
623 if (Source == PrevSource) {
624 CopyMem(&Target[-TgtWidth], Target, TgtWidth*sizeof(*Target));
625 } else {
626 ScaleLine(Target, Source, SrcWidth, TgtWidth);
627 PrevSource = Source;
629 Target += TgtWidth;
630 Source += IntPart;
631 E += FractPart;
632 if (E >= TgtHeight) {
633 E -= TgtHeight;
634 Source += SrcWidth;