Fixed typos in comments.
[AROS.git] / workbench / libs / icon / layouticon.c
blobb88efe7e482e4b6cc82ccf50e7f550243780bde5
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"
16 /* Bitmap scaling */
17 static BOOL scaleToResolution(ULONG SrcWidth, ULONG SrcHeight,
18 UWORD SrcResX, UWORD SrcResY,
19 ULONG *DstWidth, ULONG *DstHeight,
20 UWORD DstResX, UWORD DstResY,
21 ULONG *ScaleXSrc, ULONG *ScaleYSrc,
22 ULONG *ScaleXDst, ULONG *ScaleYDst,
23 struct IconBase *IconBase);
25 static BOOL scaleToBounds(ULONG SrcWidth, ULONG SrcHeight,
26 UWORD MaxWidth, UWORD MaxHeight,
27 ULONG *DstWidth, ULONG *DstHeight,
28 ULONG *ScaleXSrc, ULONG *ScaleYSrc,
29 ULONG *ScaleXDst, ULONG *ScaleYDst,
30 struct IconBase *IconBase);
32 /* ARGB image scaling */
33 static void ScaleRect(ULONG *Target, const ULONG *Source, int SrcWidth, int SrcHeight, int TgtWidth, int TgtHeight);
35 /*****************************************************************************
37 NAME */
39 AROS_LH3(BOOL, LayoutIconA,
41 /* SYNOPSIS */
42 AROS_LHA(struct DiskObject *, icon, A0),
43 AROS_LHA(struct Screen *, screen, A1),
44 AROS_LHA(struct TagItem *, tags, A2),
46 /* LOCATION */
47 struct IconBase *, IconBase, 32, Icon)
49 /* FUNCTION
50 Adapt a palette-mapped icon for display.
52 INPUTS
54 RESULT
56 NOTES
57 Not implemented.
59 EXAMPLE
61 BUGS
63 SEE ALSO
65 INTERNALS
67 *****************************************************************************/
69 AROS_LIBFUNC_INIT
71 /* Default source DPI is the Amiga PAL DPI */
72 ULONG width, height;
73 ULONG scaleXsrc = 1, scaleYsrc = 1, scaleXdst = 1, scaleYdst = 1;
74 ULONG mutualexclude = (ULONG)icon->do_Gadget.MutualExclude;
75 ULONG scalebox;
76 struct NativeIcon *ni;
77 struct RastPort rp;
78 struct DrawInfo *dri;
79 struct ColorMap *cm;
80 BOOL ret;
81 int i, j;
82 const ULONG bflags = BMF_CLEAR;
83 ni = GetNativeIcon(icon, LB(IconBase));
84 if (!ni)
85 return TRUE;
87 if (ni->ni_Width <= 0)
88 ni->ni_Width = ni->ni_DiskObject.do_Gadget.Width;
89 if (ni->ni_Height <= 0)
90 ni->ni_Height = ni->ni_DiskObject.do_Gadget.Height;
92 D(bug("[%s] Icon %p, Screen %p, %xx%x icon\n", __func__, icon, screen, ni->ni_Width, ni->ni_Height));
94 /* Already mapped to this screen?
96 if (screen == ni->ni_Screen)
97 return TRUE;
99 for (i = 0; i < 2; i++) {
100 struct NativeIconImage *image = &ni->ni_Image[i];
102 if (image->BitMap) {
103 FreeBitMap(image->BitMap);
104 image->BitMap = NULL;
107 if (image->ARGBMap && image->ARGBMap != image->ARGB) {
108 FreeVec(image->ARGBMap);
111 if (image->Pen) {
112 for (j = 0; j < image->Pens; j++) {
113 ReleasePen(ni->ni_Screen->ViewPort.ColorMap, image->Pen[j]);
115 FreeVec(image->Pen);
116 image->Pen = NULL;
119 /* Remove the synthesized BitMask */
120 if (image->BitMask) {
121 FreeVec(image->BitMask);
122 image->BitMask = NULL;
126 ni->ni_Screen = NULL;
128 if (screen == NULL)
129 return TRUE;
131 dri = GetScreenDrawInfo(screen);
132 if (dri == NULL)
133 return FALSE;
135 /* Look up the DrawInfo Pens we will need for the
136 * layout of the border and frame.
138 for (i = 0; i < NUMDRIPENS; i++) {
139 if (i < dri->dri_NumPens)
140 ni->ni_Pens[i] = dri->dri_Pens[i];
141 else
142 ni->ni_Pens[i] = dri->dri_Pens[DETAILPEN];
145 cm = screen->ViewPort.ColorMap;
147 /* NOTE: The ARGB data (if present) will not need
148 * any layout work.
150 D(bug("%s: Screen %p, Depth %d, ColorMap %d\n", __func__, screen, dri->dri_Depth, cm ? cm->Count : -1));
152 ret = TRUE;
154 /* Calculate the scaling factors
156 if (ni->ni_Face.Width && ni->ni_Face.Height) {
157 width = ni->ni_Face.Width;
158 height= ni->ni_Face.Height;
159 } else {
160 width = icon->do_Gadget.Width;
161 height = icon->do_Gadget.Height;
164 if (ni->ni_ScaleBox == ICON_SCALEBOX_DEFAULT)
165 scalebox = LB(IconBase)->ib_ScaleBox;
166 else
167 scalebox = ni->ni_ScaleBox;
169 ni->ni_Width = width;
170 ni->ni_Height = height;
172 /* Are we rescaling dynamically? */
173 if (scalebox == ICON_SCALEBOX_AUTOSCALE) {
174 UBYTE tpdX = 0, tpdY = 0;
176 /* Check for a magic MutualExlcude value
177 * that encodes Tick-Per-Dot information.
178 * MutalExclude of 0xffffffff is not valid.
180 if ((mutualexclude != 0xffffffff) && (mutualexclude & (1 << 31))) {
181 /* tpd information is in the lower 16 bits */
182 tpdX = (mutualexclude >> 8) & 0xff;
183 tpdY = (mutualexclude >> 0) & 0xff;
186 if (tpdX && tpdY) {
187 scaleToResolution(width, height, tpdX, tpdY,
188 &ni->ni_Width, &ni->ni_Height, dri->dri_Resolution.X, dri->dri_Resolution.Y,
189 &scaleXsrc, &scaleYsrc, &scaleXdst, &scaleYdst,
190 IconBase);
192 D(bug("%s: Icon tpd (%d:%d), Screen tpd (%d:%d)\n", __func__,
193 tpdX, tpdY, dri->dri_Resolution.X, dri->dri_Resolution.Y));
195 } else {
196 WORD MaxWidth, MaxHeight;
198 UNPACK_ICON_SCALEBOX(scalebox, MaxWidth, MaxHeight);
200 scaleToBounds(width, height, MaxWidth, MaxHeight,
201 &ni->ni_Width, &ni->ni_Height,
202 &scaleXsrc, &scaleYsrc, &scaleXdst, &scaleYdst,
203 IconBase);
206 for (i = 0; i < 2; i++) {
207 struct NativeIconImage *image = &ni->ni_Image[i];
208 struct TagItem pentags[] = {
209 { OBP_Precision, IconBase->ib_Precision },
210 { OBP_FailIfBad, FALSE },
211 { TAG_MORE, (IPTR)tags },
213 UBYTE *idata;
214 ULONG x;
215 UWORD bmdepth;
217 bmdepth = GetBitMapAttr(screen->RastPort.BitMap, BMA_DEPTH);
219 /* If we can use ARGB data, then use it! */
220 D(bug("[%s] Screen depth is %d\n", __func__, bmdepth));
221 if ((bmdepth > 8) && CyberGfxBase) {
222 FetchIconARGB(icon, i);
223 if (image->ARGB) {
224 if (width != ni->ni_Width || height != ni->ni_Height) {
225 if ((image->ARGBMap = AllocVec(ni->ni_Width * ni->ni_Height * sizeof(ULONG), MEMF_PUBLIC))) {
226 D(bug("[%s] ARGB scaling\n"));
227 ScaleRect(image->ARGBMap, image->ARGB, width, height, ni->ni_Width, ni->ni_Height);
228 continue;
230 } else {
231 image->ARGBMap = (APTR)image->ARGB;
234 if (!image->ARGBMap) {
235 D(bug("[%s] No ARGB image\n"));
239 /* Allocate a bitmap, which is a 'friend' of the screen */
240 image->BitMap = AllocBitMap(width, height, dri->dri_Depth, bflags, screen->RastPort.BitMap);
241 if (image->BitMap == NULL) {
242 SetIoErr(ERROR_NO_FREE_STORE);
243 ret = FALSE;
244 goto exit;
247 FetchIconImage(icon, i);
249 if (!image->ImageData) {
250 struct Image *gi = NULL;
251 ULONG state;
252 BOOL flood = FALSE;
254 if (i == 1 && (icon->do_Gadget.Flags & GFLG_GADGHIMAGE)) {
255 gi = icon->do_Gadget.SelectRender;
256 } else if (icon->do_Gadget.Flags & GFLG_GADGIMAGE) {
257 gi = icon->do_Gadget.GadgetRender;
260 if (gi == NULL)
261 continue;
263 if (i == 1 && (icon->do_Gadget.Flags & GFLG_GADGHCOMP))
264 state = IDS_SELECTED;
265 else
266 state = IDS_NORMAL;
268 if (i == 1 && (icon->do_Gadget.Flags & GFLG_GADGBACKFILL)) {
269 state = IDS_SELECTED;
270 flood = TRUE;
273 InitRastPort(&rp);
274 if (!flood) {
275 rp.BitMap = image->BitMap;
276 DrawImageState(&rp, gi, 0, 0, state, dri);
277 } else {
278 /* Create a bitmap with a 1 pixel border,
279 * fill it with the inverse color,
280 * draw the inverse image into it,
281 * then flood-fill the border with color 0.
283 * Finally, copy the final image to the
284 * destination bitmap.
286 struct BitMap *bm;
287 if ((bm = AllocBitMap(gi->Width+2, gi->Height+2, gi->Depth, BMF_CLEAR, NULL))) {
288 PLANEPTR trbuf;
290 rp.BitMap = bm;
292 if ((trbuf = AllocRaster(gi->Width+2, gi->Height+2))) {
293 struct TmpRas tr;
294 InitTmpRas(&tr, trbuf, RASSIZE(gi->Width+2, gi->Height+2));
295 rp.TmpRas = &tr;
296 SetAPen(&rp, (1 << gi->Depth)-1);
297 RectFill(&rp, 0, 0, gi->Width+1, gi->Height+1);
298 DrawImageState(&rp, gi, 1, 1, state, dri);
299 SetAPen(&rp, 0);
300 Flood(&rp, 1, 0, 0);
301 BltBitMap(bm, 1, 1, image->BitMap, 0, 0, gi->Width, gi->Height, 0xc0, ~0, NULL);
302 FreeRaster(trbuf, gi->Width+2, gi->Height+2);
304 FreeBitMap(bm);
308 goto rescale;
311 /* Palettized image processing */
312 if (!image->Pen)
313 image->Pen = AllocVec(image->Pens * sizeof(image->Pen[0]), MEMF_PUBLIC | MEMF_CLEAR);
315 if (!image->Pen) {
316 SetIoErr(ERROR_NO_FREE_STORE);
317 ret = FALSE;
318 goto exit;
321 /* Get the needed colormap entries. */
322 for (j = 0; j < image->Pens; j++) {
323 ULONG r,g,b;
324 LONG pen;
325 /* CHECKME: So, uh, how does one accuarately
326 * convert 8 bit RGB to 32 bit RBG?
328 r = image->Palette[j].red << 24;
329 g = image->Palette[j].green << 24;
330 b = image->Palette[j].blue << 24;
331 pen = ObtainBestPenA(cm, r, g, b, pentags);
332 image->Pen[j] = (UBYTE)pen;
335 /* Draw the selected state into the screen's pens
337 * We take the risk of yet another memory allocation
338 * so that we can use WriteChunkyPixels(), which is
339 * GOBS faster than WritePixel().
341 idata = AllocVec(height * width, MEMF_ANY);
342 if (idata == NULL) {
343 FreeBitMap(image->BitMap);
344 image->BitMap = NULL;
345 SetIoErr(ERROR_NO_FREE_STORE);
346 ret = FALSE;
347 goto exit;
349 CopyMem(image->ImageData, idata, height * width);
350 for (x = 0; x < (height * width); x++) {
351 idata[x] = image->Pen[image->ImageData[x]];
353 InitRastPort(&rp);
354 rp.BitMap = image->BitMap;
355 WriteChunkyPixels(&rp, 0, 0, width - 1, height - 1,
356 idata, width);
357 FreeVec(idata);
359 /* Synthesize a bitmask for transparentcolor icons */
360 D(bug("[%s] TransparentColor %d\n", __func__, image->TransparentColor));
361 if (image->TransparentColor >= 0) {
362 int x, y;
363 UBYTE *row;
364 CONST UBYTE *img;
365 UWORD bpr = image->BitMap->BytesPerRow;
366 image->BitMask = AllocVec(bpr * height + 4, MEMF_PUBLIC | MEMF_CHIP | MEMF_CLEAR);
367 if (!image->BitMask) {
368 SetIoErr(ERROR_NO_FREE_STORE);
369 return FALSE;
372 img = image->ImageData;
373 row = image->BitMask;
374 #ifdef __mc68000 /* AGA support */
375 row = (APTR)(((IPTR)row + 7) & ~7);
376 #endif
377 for (y = 0; y < height; y++, row += bpr) {
378 for (x = 0; x < width; x++, img++) {
379 if ((*img != image->TransparentColor)) {
380 row[x>>3] |= 1 << (7 - (x & 7));
386 rescale:
387 if (width != ni->ni_Width || height != ni->ni_Height) {
388 struct BitMap *bm = AllocBitMap(ni->ni_Width, ni->ni_Height, dri->dri_Depth, bflags, image->BitMap);
390 D(bug("%s: Rescaling from %dx%d to %dx%d\n", __func__,
391 width, height, ni->ni_Width, ni->ni_Height));
393 if (bm) {
394 struct BitScaleArgs bsa = {
395 .bsa_SrcBitMap = image->BitMap,
396 .bsa_SrcX = 0,
397 .bsa_SrcY = 0,
398 .bsa_SrcWidth = width,
399 .bsa_SrcHeight = height,
400 .bsa_XSrcFactor = scaleXsrc,
401 .bsa_XDestFactor = scaleXdst,
402 .bsa_YSrcFactor = scaleYsrc,
403 .bsa_YDestFactor = scaleYdst,
404 .bsa_DestBitMap = bm,
406 BitMapScale(&bsa);
408 if (image->BitMask) {
409 struct BitMap src, dst;
410 PLANEPTR dst_mask;
411 src.BytesPerRow = image->BitMap->BytesPerRow;
412 src.Rows = height;
413 src.Flags = 0;
414 src.Depth = 1;
415 src.Planes[0] = image->BitMask;
416 #ifdef __mc68000 /* AGA support */
417 src.Planes[0] = (APTR)(((IPTR)src.Planes[0] + 7) & ~7);
418 #endif
419 dst.BytesPerRow = bm->BytesPerRow;
420 dst.Rows = ni->ni_Height;
421 dst.Flags = 0;
422 dst.Depth = 1;
423 dst_mask = AllocVec(dst.BytesPerRow * dst.Rows + 4, MEMF_PUBLIC | MEMF_CHIP | MEMF_CLEAR);
424 dst.Planes[0] = dst_mask;
425 if (dst.Planes[0]) {
426 #ifdef __mc68000 /* AGA support */
427 dst.Planes[0] = (APTR)(((IPTR)dst.Planes[0] + 7) & ~7);
428 #endif
429 bsa.bsa_SrcBitMap = &src;
430 bsa.bsa_DestBitMap = &dst;
431 bsa.bsa_SrcX = 0,
432 bsa.bsa_SrcY = 0,
433 bsa.bsa_SrcWidth = width,
434 bsa.bsa_SrcHeight = height,
435 bsa.bsa_XSrcFactor = scaleXsrc,
436 bsa.bsa_XDestFactor = scaleXdst,
437 bsa.bsa_YSrcFactor = scaleYsrc,
438 bsa.bsa_YDestFactor = scaleYdst,
440 BitMapScale(&bsa);
441 FreeVec(image->BitMask);
442 image->BitMask = dst_mask;
443 } else {
444 FreeVec(dst_mask);
445 FreeBitMap(bm);
446 continue;
450 FreeBitMap(image->BitMap);
451 image->BitMap = bm;
456 ni->ni_Screen = screen;
458 exit:
459 FreeScreenDrawInfo(screen, dri);
461 if (ret == TRUE)
462 SetIoErr(0);
464 return ret;
466 AROS_LIBFUNC_EXIT
467 } /* LayoutIconA() */
470 static BOOL scaleToBounds(ULONG SrcWidth, ULONG SrcHeight,
471 UWORD MaxWidth, UWORD MaxHeight,
472 ULONG *DstWidth, ULONG *DstHeight,
473 ULONG *ScaleXSrc, ULONG *ScaleYSrc,
474 ULONG *ScaleXDst, ULONG *ScaleYDst,
475 struct IconBase *IconBase)
477 ULONG scaleXsrc, scaleYsrc, scaleXdst, scaleYdst;
479 if (MaxWidth <= 0 || MaxHeight <= 0)
480 return FALSE;
482 /* Scaling calculations
484 scaleXsrc = SrcWidth;
485 scaleYsrc = SrcHeight;
486 scaleXdst = MaxWidth;
487 scaleYdst = SrcHeight * MaxWidth / SrcWidth;
489 if (scaleYdst > MaxHeight) {
490 LONG delta = scaleYdst - MaxHeight;
491 scaleXdst -= delta * SrcWidth / SrcHeight;
492 scaleYdst -= delta;
495 while (scaleXsrc > 168383 || scaleXdst > 168383) {
496 scaleXsrc >>= 1;
497 scaleXdst >>= 1;
498 if (scaleXsrc == 0 || scaleXdst == 0) {
499 D(bug("\tCan't scale X from %dx%d to %dx%d\n", scaleXsrc, scaleYsrc, scaleXdst, scaleYdst));
500 return FALSE;
504 while (scaleYsrc > 168383 || scaleYdst > 168383) {
505 scaleYsrc >>= 1;
506 scaleYdst >>= 1;
507 if (scaleYsrc == 0 || scaleYdst == 0) {
508 D(bug("\tCan't scale Y from %dx%d to %dx%d\n", scaleXsrc, scaleYsrc, scaleXdst, scaleYdst));
509 return FALSE;
513 *DstWidth = ScalerDiv(SrcWidth, scaleXdst, scaleXsrc);
514 *DstHeight = ScalerDiv(SrcHeight, scaleYdst, scaleYsrc);
516 *ScaleXSrc = scaleXsrc;
517 *ScaleYSrc = scaleYsrc;
519 *ScaleXDst = scaleXdst;
520 *ScaleYDst = scaleYdst;
522 D(bug("[%s] Scale icon %dx%d to box %dx%d => %dx%d\n", __func__, SrcWidth, SrcHeight, MaxWidth, MaxHeight, *DstWidth, *DstHeight));
524 return TRUE;
527 static BOOL scaleToResolution(ULONG SrcWidth, ULONG SrcHeight,
528 UWORD SrcResX, UWORD SrcResY,
529 ULONG *DstWidth, ULONG *DstHeight,
530 UWORD DstResX, UWORD DstResY,
531 ULONG *ScaleXSrc, ULONG *ScaleYSrc,
532 ULONG *ScaleXDst, ULONG *ScaleYDst,
533 struct IconBase *IconBase)
535 ULONG scaleXsrc, scaleYsrc, scaleXdst, scaleYdst;
537 /* Scaling calculations
538 * Remember: 'res' is in 'ticks', which is inversely
539 * related to display DPI.
541 scaleXsrc = SrcWidth;
542 scaleYsrc = SrcHeight;
543 scaleXdst = SrcWidth * SrcResX / DstResX;
544 scaleYdst = SrcHeight * SrcResY / DstResY;
546 while (scaleXsrc > 168383 || scaleXdst > 168383) {
547 scaleXsrc >>= 1;
548 scaleXdst >>= 1;
549 if (scaleXsrc == 0 || scaleXdst == 0) {
550 D(bug("\tCan't scale X from %dx%d to %dx%d\n", scaleXsrc, scaleYsrc, scaleXdst, scaleYdst));
551 return FALSE;
555 while (scaleYsrc > 168383 || scaleYdst > 168383) {
556 scaleYsrc >>= 1;
557 scaleYdst >>= 1;
558 if (scaleYsrc == 0 || scaleYdst == 0) {
559 D(bug("\tCan't scale Y from %dx%d to %dx%d\n", scaleXsrc, scaleYsrc, scaleXdst, scaleYdst));
560 return FALSE;
564 *DstWidth = ScalerDiv(SrcWidth, scaleXdst, scaleXsrc);
565 *DstHeight = ScalerDiv(SrcHeight, scaleYdst, scaleYsrc);
567 *ScaleXSrc = scaleXsrc;
568 *ScaleYSrc = scaleYsrc;
570 *ScaleXDst = scaleXdst;
571 *ScaleYDst = scaleYdst;
573 D(bug("[%s] Scale icon %dx%d => %dx%d\n", __func__, SrcWidth, SrcHeight, *DstWidth, *DstHeight));
575 return TRUE;
578 /* From 'Image Scaling With Bresenham', Dr. Dobbs Journal, May 1, 2002
580 static inline void ScaleLine(ULONG *Target, const ULONG *Source, int SrcWidth, int TgtWidth)
582 int NumPixels = TgtWidth;
583 int IntPart = SrcWidth / TgtWidth;
584 int FracPart = SrcWidth % TgtWidth;
585 int E = 0;
586 while (NumPixels-- > 0) {
587 *(Target++) = *Source;
588 Source += IntPart;
589 E += FracPart;
590 if (E >= TgtWidth) {
591 E -= TgtWidth;
592 Source++;
597 static void ScaleRect(ULONG *Target, const ULONG *Source, int SrcWidth, int SrcHeight, int TgtWidth, int TgtHeight)
599 int NumPixels = TgtHeight;
600 int IntPart = (SrcHeight / TgtHeight) * SrcWidth;
601 int FractPart = SrcHeight % TgtHeight;
602 int E = 0;
603 const ULONG *PrevSource = NULL;
604 while (NumPixels-- > 0) {
605 if (Source == PrevSource) {
606 CopyMem(&Target[-TgtWidth], Target, TgtWidth*sizeof(*Target));
607 } else {
608 ScaleLine(Target, Source, SrcWidth, TgtWidth);
609 PrevSource = Source;
611 Target += TgtWidth;
612 Source += IntPart;
613 E += FractPart;
614 if (E >= TgtHeight) {
615 E -= TgtHeight;
616 Source += SrcWidth;