2002-03-27 23:44:41 +00:00
|
|
|
/*
|
|
|
|
XGBitmapImageRep.m
|
|
|
|
|
|
|
|
NSBitmapImageRep for GNUstep GUI X/GPS Backend
|
|
|
|
|
|
|
|
Copyright (C) 1996-1999 Free Software Foundation, Inc.
|
|
|
|
|
|
|
|
Author: Adam Fedor <fedor@colorado.edu>
|
|
|
|
Author: Scott Christley <scottc@net-community.com>
|
|
|
|
Date: Feb 1996
|
|
|
|
Author: Felipe A. Rodriguez <far@ix.netcom.com>
|
|
|
|
Date: May 1998
|
|
|
|
Author: Richard Frith-Macdonald <richard@brainstorm.co.uk>
|
|
|
|
Date: Mar 1999
|
|
|
|
|
|
|
|
This file is part of the GNUstep GUI X/GPS Backend.
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Library General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
|
|
License along with this library; see the file COPYING.LIB.
|
|
|
|
If not, write to the Free Software Foundation,
|
|
|
|
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include <X11/Xlib.h>
|
|
|
|
#include <X11/Xutil.h>
|
|
|
|
|
2003-09-21 03:19:17 +00:00
|
|
|
#include <Foundation/NSData.h>
|
2002-03-27 23:44:41 +00:00
|
|
|
#include <Foundation/NSDebug.h>
|
2002-05-10 15:06:17 +00:00
|
|
|
#include "gsc/gscolors.h"
|
2002-03-27 23:44:41 +00:00
|
|
|
#include "xlib/XGPrivate.h"
|
|
|
|
|
|
|
|
/* Macros that make it easier to convert
|
|
|
|
between (r,g,b) <--> pixels.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define VARIABLES_DECLARATION \
|
|
|
|
unsigned char _rshift, _gshift, _bshift, _ashift; \
|
|
|
|
unsigned int _rmask, _gmask, _bmask, _amask; \
|
|
|
|
unsigned char _rwidth, _gwidth, _bwidth, _awidth
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define InitRGBShiftsAndMasks(rs,rw,gs,gw,bs,bw,as,aw) \
|
|
|
|
do { \
|
2002-07-15 02:50:59 +00:00
|
|
|
_rshift = (rs); \
|
|
|
|
_rmask = (1<<(rw)) -1; \
|
|
|
|
_rwidth = (rw); \
|
|
|
|
_gshift = (gs); \
|
|
|
|
_gmask = (1<<(gw)) -1; \
|
|
|
|
_gwidth = (gw); \
|
|
|
|
_bshift = (bs); \
|
|
|
|
_bmask = (1<<(bw)) -1; \
|
|
|
|
_bwidth = (bw); \
|
|
|
|
_amask = (1<<(aw)) -1; \
|
|
|
|
_ashift = (as); \
|
|
|
|
_awidth = (aw); \
|
2002-03-27 23:44:41 +00:00
|
|
|
} while(0)
|
|
|
|
|
|
|
|
|
|
|
|
#define PixelToRGB(pixel,r,g,b) \
|
|
|
|
do { \
|
2002-07-15 02:50:59 +00:00
|
|
|
(r) = (pixel >> _rshift) & _rmask; \
|
|
|
|
(g) = (pixel >> _gshift) & _gmask; \
|
|
|
|
(b) = (pixel >> _bshift) & _bmask; \
|
2002-03-27 23:44:41 +00:00
|
|
|
} while(0)
|
|
|
|
|
|
|
|
/* Note that RGBToPixel assumes that the
|
|
|
|
r,g,b values are in the correct domain.
|
|
|
|
If not, the value is nonsense.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define RGBToPixel(r,g,b, pixel) \
|
|
|
|
do { \
|
|
|
|
pixel = ((r) << _rshift) \
|
|
|
|
|((g) << _gshift) \
|
|
|
|
|((b) << _bshift); \
|
|
|
|
} while(0)
|
|
|
|
|
2002-07-15 02:50:59 +00:00
|
|
|
#define CLAMP(a) \
|
|
|
|
do { \
|
|
|
|
(a) = MAX(0, MIN(255, (a))); \
|
|
|
|
} while (0)
|
2002-03-27 23:44:41 +00:00
|
|
|
|
2003-10-20 03:45:55 +00:00
|
|
|
#define CSIZE 16384
|
|
|
|
#define GS_QUERY_COLOR(color) \
|
|
|
|
do { \
|
|
|
|
int centry = color.pixel % CSIZE; \
|
|
|
|
if (empty[centry] == NO && pixels[centry] == color.pixel) \
|
|
|
|
{ \
|
|
|
|
color = colors[centry]; \
|
|
|
|
} \
|
|
|
|
else \
|
|
|
|
{ \
|
|
|
|
empty[centry] = NO; \
|
|
|
|
XQueryColor(context->dpy, context->cmap, &color); \
|
|
|
|
pixels[centry] = color.pixel; \
|
|
|
|
colors[centry] = color; \
|
|
|
|
} \
|
|
|
|
} while(0)
|
|
|
|
|
2002-03-27 23:44:41 +00:00
|
|
|
/* Composite source image (pixmap) onto a destination image with alpha.
|
|
|
|
Only works for op=Sover now
|
|
|
|
|
|
|
|
Assumptions:
|
|
|
|
- all images are valid.
|
|
|
|
- srect is completely contained in the source images
|
|
|
|
- srect put at the origin is contained in the destination image
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
_pixmap_combine_alpha(RContext *context,
|
|
|
|
RXImage *source_im, RXImage *source_alpha,
|
|
|
|
RXImage *dest_im, RXImage *dest_alpha,
|
|
|
|
XRectangle srect,
|
|
|
|
NSCompositingOperation op,
|
|
|
|
XGDrawMechanism drawMechanism,
|
|
|
|
float fraction)
|
|
|
|
{
|
|
|
|
unsigned long pixel;
|
|
|
|
unsigned long oldPixel = 0;
|
2002-07-15 02:50:59 +00:00
|
|
|
|
|
|
|
fraction = MAX(0.0, MIN(1.0, fraction));
|
2002-03-27 23:44:41 +00:00
|
|
|
|
|
|
|
if (drawMechanism == XGDM_FAST15
|
|
|
|
|| drawMechanism == XGDM_FAST16
|
|
|
|
|| drawMechanism == XGDM_FAST32
|
|
|
|
|| drawMechanism == XGDM_FAST32_BGR)
|
|
|
|
{
|
|
|
|
VARIABLES_DECLARATION;
|
|
|
|
unsigned row;
|
|
|
|
|
|
|
|
switch (drawMechanism)
|
|
|
|
{
|
|
|
|
case XGDM_FAST15:
|
|
|
|
InitRGBShiftsAndMasks(10,5,5,5,0,5,0,8);
|
|
|
|
break;
|
|
|
|
case XGDM_FAST16:
|
|
|
|
InitRGBShiftsAndMasks(11,5,5,6,0,5,0,8);
|
|
|
|
break;
|
|
|
|
case XGDM_FAST32:
|
|
|
|
InitRGBShiftsAndMasks(16,8,8,8,0,8,0,8);
|
|
|
|
break;
|
|
|
|
case XGDM_FAST32_BGR:
|
|
|
|
InitRGBShiftsAndMasks(0,8,8,8,16,8,0,8);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
NSLog(@"Huh? Backend confused about XGDrawMechanism");
|
|
|
|
//Try something. With a bit of luck we see
|
|
|
|
//which picture goes wrong.
|
|
|
|
InitRGBShiftsAndMasks(11,5,5,6,0,5,0,8);
|
|
|
|
}
|
2002-07-15 02:50:59 +00:00
|
|
|
|
2002-03-27 23:44:41 +00:00
|
|
|
for (row = 0; row < srect.height; row++)
|
|
|
|
{
|
|
|
|
unsigned col;
|
|
|
|
|
|
|
|
for (col = 0; col < srect.width; col++)
|
|
|
|
{
|
2002-08-03 03:32:19 +00:00
|
|
|
unsigned sr, sg, sb, sa; // source
|
|
|
|
unsigned dr, dg, db, da; // dest
|
|
|
|
unsigned ialpha;
|
2002-03-27 23:44:41 +00:00
|
|
|
|
2002-07-15 02:50:59 +00:00
|
|
|
// Get the source pixel information
|
2002-08-03 03:32:19 +00:00
|
|
|
pixel = XGetPixel(source_im->image, srect.x+col, srect.y+row);
|
|
|
|
PixelToRGB(pixel, sr, sg, sb);
|
2002-03-27 23:44:41 +00:00
|
|
|
|
|
|
|
if (source_alpha)
|
2002-07-15 02:50:59 +00:00
|
|
|
{
|
2002-08-03 03:32:19 +00:00
|
|
|
pixel = XGetPixel(source_alpha->image,
|
|
|
|
srect.x + col, srect.y + row);
|
2002-07-15 02:50:59 +00:00
|
|
|
sa = (pixel >> _ashift) & _amask;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
sa = _amask;
|
2002-03-27 23:44:41 +00:00
|
|
|
|
2002-07-15 02:50:59 +00:00
|
|
|
if (sa == 0) // dest wouldn't be changed
|
|
|
|
continue;
|
2002-03-27 23:44:41 +00:00
|
|
|
|
2002-07-15 02:50:59 +00:00
|
|
|
if (fraction < 1.0)
|
|
|
|
sa *= fraction;
|
|
|
|
|
2002-08-03 03:32:19 +00:00
|
|
|
sr *= sa;
|
|
|
|
sg *= sa;
|
|
|
|
sb *= sa;
|
|
|
|
|
|
|
|
ialpha = (_amask - sa);
|
2002-07-15 02:50:59 +00:00
|
|
|
|
|
|
|
// Now get dest pixel
|
2002-08-03 03:32:19 +00:00
|
|
|
pixel = XGetPixel(dest_im->image, col, row);
|
|
|
|
PixelToRGB(pixel, dr, dg, db);
|
2002-07-15 02:50:59 +00:00
|
|
|
|
2002-08-03 03:32:19 +00:00
|
|
|
dr *= ialpha;
|
|
|
|
dg *= ialpha;
|
|
|
|
db *= ialpha;
|
|
|
|
|
2002-07-15 02:50:59 +00:00
|
|
|
if (dest_alpha)
|
2002-08-03 03:32:19 +00:00
|
|
|
{
|
2002-07-15 02:50:59 +00:00
|
|
|
pixel = XGetPixel(dest_alpha->image, col, row);
|
|
|
|
da = (pixel >> _ashift) & _amask;
|
2002-08-03 03:32:19 +00:00
|
|
|
}
|
2002-07-15 02:50:59 +00:00
|
|
|
else // no alpha channel, background is opaque
|
|
|
|
da = _amask;
|
2002-08-03 03:32:19 +00:00
|
|
|
|
|
|
|
dr = (sr + dr) / _amask;
|
|
|
|
dg = (sg + dg) / _amask;
|
|
|
|
db = (sb + db) / _amask;
|
2002-07-15 02:50:59 +00:00
|
|
|
|
|
|
|
// calc final alpha
|
|
|
|
if (sa == _amask || da == _amask)
|
|
|
|
da = _amask;
|
|
|
|
else
|
2002-08-03 03:32:19 +00:00
|
|
|
da = sa + ((da * ialpha) / _amask);
|
2002-07-15 02:50:59 +00:00
|
|
|
|
|
|
|
CLAMP(dr);
|
|
|
|
CLAMP(dg);
|
|
|
|
CLAMP(db);
|
|
|
|
CLAMP(da);
|
|
|
|
|
|
|
|
RGBToPixel(dr, dg, db, pixel);
|
2002-03-27 23:44:41 +00:00
|
|
|
XPutPixel(dest_im->image, col, row, pixel);
|
|
|
|
if (dest_alpha)
|
2002-07-15 02:50:59 +00:00
|
|
|
XPutPixel(dest_alpha->image, col, row, da << _ashift);
|
2002-03-27 23:44:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2003-10-20 03:45:55 +00:00
|
|
|
unsigned long pixels[CSIZE];
|
|
|
|
XColor colors[CSIZE];
|
|
|
|
BOOL empty[CSIZE];
|
2002-03-27 23:44:41 +00:00
|
|
|
XColor c2;
|
|
|
|
unsigned row;
|
2003-10-20 03:45:55 +00:00
|
|
|
int cind;
|
2002-03-27 23:44:41 +00:00
|
|
|
|
2003-10-20 03:45:55 +00:00
|
|
|
for(cind = 0; cind < CSIZE; cind++)
|
|
|
|
{
|
|
|
|
empty[cind] = YES;
|
|
|
|
}
|
|
|
|
|
2002-03-27 23:44:41 +00:00
|
|
|
/*
|
|
|
|
* This block of code should be totally portable as it uses the
|
|
|
|
* 'official' X mechanism for converting from pixel values to
|
|
|
|
* RGB color values - on the downside, it's very slow.
|
|
|
|
*/
|
|
|
|
pixel = (unsigned long)-1; // Never valid?
|
|
|
|
c2.pixel = pixel;
|
|
|
|
|
|
|
|
for (row = 0; row < srect.height; row++)
|
|
|
|
{
|
|
|
|
unsigned col;
|
|
|
|
|
|
|
|
for (col = 0; col < srect.width; col++)
|
|
|
|
{
|
|
|
|
int r, g, b, alpha;
|
|
|
|
XColor pcolor, acolor, c0, c1;
|
|
|
|
pcolor.pixel = XGetPixel(source_im->image,
|
|
|
|
col + srect.x, row + srect.y);
|
2003-10-20 03:45:55 +00:00
|
|
|
GS_QUERY_COLOR(pcolor);
|
2002-03-27 23:44:41 +00:00
|
|
|
r = pcolor.red >> 8;
|
|
|
|
g = pcolor.green >> 8;
|
|
|
|
b = pcolor.blue >> 8;
|
|
|
|
alpha = 255;
|
|
|
|
if (source_alpha)
|
|
|
|
{
|
|
|
|
acolor.pixel = XGetPixel(source_alpha->image,
|
|
|
|
col + srect.x, row + srect.y);
|
2003-10-20 03:45:55 +00:00
|
|
|
GS_QUERY_COLOR(acolor);
|
2002-03-27 23:44:41 +00:00
|
|
|
alpha = acolor.red >> 8;
|
|
|
|
}
|
|
|
|
if (alpha == 0)
|
|
|
|
continue; // background unchanged.
|
|
|
|
|
|
|
|
if (fraction < 1)
|
|
|
|
alpha *= fraction;
|
|
|
|
if (alpha == 255)
|
|
|
|
{
|
|
|
|
c1 = pcolor;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
unsigned short ialpha = 255 - alpha;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* RGB color components in X are 16-bit values -
|
|
|
|
* but our bitmap contains 8-bit values so we must
|
|
|
|
* adjust our values up to 16-bit, which we can do
|
|
|
|
* by increasing their alpha component by 256.
|
|
|
|
*/
|
|
|
|
alpha <<= 8;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the background pixel and convert to RGB if
|
|
|
|
* we haven't already done the conversion earlier.
|
|
|
|
*/
|
|
|
|
c0.pixel = XGetPixel(dest_im->image, col, row);
|
|
|
|
if (c0.pixel != oldPixel)
|
|
|
|
{
|
|
|
|
oldPixel = c0.pixel;
|
2003-10-20 03:45:55 +00:00
|
|
|
GS_QUERY_COLOR(c0);
|
2002-03-27 23:44:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// mix in alpha to produce RGB out
|
|
|
|
c1.red = ((c0.red*ialpha) + (r*alpha))/255;
|
|
|
|
c1.green = ((c0.green*ialpha) + (g*alpha))/255;
|
|
|
|
c1.blue = ((c0.blue*ialpha) + (b*alpha))/255;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert the X RGB value to a colormap entry if we
|
|
|
|
* don't already have one. Then set the pixel.
|
2003-11-20 16:29:32 +00:00
|
|
|
* NB. We will not necessarily get a
|
2002-03-27 23:44:41 +00:00
|
|
|
* color with exactly the rgb components we gave it, so
|
|
|
|
* we must record those components beforehand.
|
|
|
|
*/
|
|
|
|
if (c2.pixel == (unsigned long)-1
|
|
|
|
|| c2.red != c1.red
|
|
|
|
|| c2.green != c1.green
|
|
|
|
|| c2.blue != c1.blue)
|
|
|
|
{
|
2003-11-20 16:29:32 +00:00
|
|
|
RColor rc;
|
|
|
|
c2 = c1;
|
|
|
|
rc.red = c1.red >> 8;
|
|
|
|
rc.green = c1.green >> 8;
|
|
|
|
rc.blue = c1.blue >> 8;
|
|
|
|
RGetClosestXColor(context, &rc, &c1);
|
2002-03-27 23:44:41 +00:00
|
|
|
c2.pixel = c1.pixel;
|
|
|
|
}
|
|
|
|
XPutPixel(dest_im->image, col, row, c1.pixel);
|
|
|
|
if (dest_alpha)
|
|
|
|
{
|
2003-11-20 16:29:32 +00:00
|
|
|
RColor rc;
|
2002-03-27 23:44:41 +00:00
|
|
|
XColor da;
|
|
|
|
/* Alpha gets mixed the same as all the
|
|
|
|
other color components */
|
|
|
|
da.pixel = XGetPixel(dest_alpha->image, col, row);
|
2003-10-20 03:45:55 +00:00
|
|
|
GS_QUERY_COLOR(da);
|
2003-11-20 16:29:32 +00:00
|
|
|
rc.red = acolor.red >> 8;
|
|
|
|
rc.red = rc.red + (da.red >> 8) * (256 - rc.red)/256;
|
|
|
|
rc.green = rc.blue = rc.red;
|
|
|
|
RGetClosestXColor(context, &rc, &da);
|
2002-03-27 23:44:41 +00:00
|
|
|
XPutPixel(dest_alpha->image, col, row, da.pixel);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The following structure holds the information necessary for unpacking
|
|
|
|
* a bitmap data object. It holds an index into the raw data, precalculated
|
|
|
|
* spans for magnification and minifaction, plus the buffer which holds
|
|
|
|
* a complete line of colour to be output to the screen in RGBA form.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct _bitmap_decompose {
|
|
|
|
unsigned char *plane[5];
|
|
|
|
int bit_off[5];
|
|
|
|
long image_w, image_h, screen_w, screen_h;
|
|
|
|
int bps, spp, bpp, bpr;
|
|
|
|
BOOL has_alpha, is_direct_packed, one_is_black;
|
|
|
|
int cspace, pro_mul;
|
|
|
|
|
|
|
|
unsigned char *r, *g, *b, *a;
|
|
|
|
int cur_image_row, cur_screen_row;
|
|
|
|
int first_vis_col, last_vis_col;
|
|
|
|
|
|
|
|
int *row_starts, *row_ends, *col_starts, *col_ends;
|
|
|
|
int *r_sum, *g_sum, *b_sum, *a_sum, *pix_count;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Here we extract a value a given number of bits wide from a bit
|
|
|
|
* offset into a block of memory starting at "base". The bit numbering
|
|
|
|
* is assumed to be such that a bit offset of zero and a width of 4 gives
|
|
|
|
* the upper 4 bits of the first byte, *not* the lower 4 bits. We do allow
|
|
|
|
* the value to cross a byte boundary, though it is unclear as to whether
|
|
|
|
* this is strictly necessary for OpenStep tiffs.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int
|
|
|
|
_get_bit_value(unsigned char *base, long msb_off, int bit_width)
|
|
|
|
{
|
|
|
|
long lsb_off, byte1, byte2;
|
|
|
|
int shift, value;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Firstly we calculate the position of the msb and lsb in terms
|
|
|
|
* of bit offsets and thus byte offsets. The shift is the number of
|
|
|
|
* spare bits left in the byte containing the lsb
|
|
|
|
*/
|
|
|
|
lsb_off= msb_off+bit_width-1;
|
|
|
|
byte1= msb_off/8;
|
|
|
|
byte2= lsb_off/8;
|
|
|
|
shift= 7-(lsb_off%8);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We now get the value from the byte array, possibly using two bytes if
|
|
|
|
* the required set of bits crosses the byte boundary. This is then shifted
|
|
|
|
* down to it's correct position and extraneous bits masked off before
|
|
|
|
* being returned.
|
|
|
|
*/
|
|
|
|
value=base[byte2];
|
|
|
|
if(byte1!=byte2)
|
|
|
|
value|= base[byte1]<<8;
|
|
|
|
value >>= shift;
|
|
|
|
|
|
|
|
return value & ((1<<bit_width)-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Extract a single pixel from a row. We are passed addresses for the red,
|
|
|
|
* green, blue and aalpha components, along with the rwnumber and all the
|
|
|
|
* necessary information to access the raw data. This function is responsible
|
|
|
|
* for extracting the raw data and converting it to 8 bit RGB for return so
|
|
|
|
* as to present a unified interface to the higher functions.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
_get_image_pixel(int col, unsigned char *r, unsigned char *g,
|
|
|
|
unsigned char *b, unsigned char *a,
|
|
|
|
unsigned char **planes, int *bit_off,
|
|
|
|
int spp, int bpp, int bps,
|
|
|
|
int pro_mul, int cspace, BOOL has_alpha, BOOL one_is_black )
|
|
|
|
{
|
|
|
|
int p, values[5];
|
|
|
|
long off = col * bpp;
|
|
|
|
|
|
|
|
for(p = 0; p < spp; p++)
|
|
|
|
{
|
|
|
|
int raw_value = _get_bit_value(planes[p], off + bit_off[p], bps);
|
|
|
|
values[p] = raw_value * pro_mul;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* deal with the alpha */
|
|
|
|
*a = has_alpha ? values[spp-1] : 255;
|
|
|
|
|
|
|
|
/* handle the colourspace */
|
|
|
|
switch(cspace)
|
|
|
|
{
|
|
|
|
case rgb_colorspace:
|
|
|
|
*r = values[0];
|
|
|
|
*g = values[1];
|
|
|
|
*b = values[2];
|
|
|
|
break;
|
|
|
|
case cmyk_colorspace:
|
|
|
|
*r = 255 - (values[0] + values[3]);
|
|
|
|
*g = 255 - (values[1] + values[3]);
|
|
|
|
*b = 255 - (values[2] + values[3]);
|
|
|
|
break;
|
|
|
|
case gray_colorspace:
|
|
|
|
*r = *g = *b = (one_is_black ? (255 - values[0]) : values[0]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Main image decomposing function. This function creates the next row
|
|
|
|
* in the image that needs to be output to the screen. For direct packed
|
|
|
|
* images it simply copies the data directly, for all others it runs through
|
|
|
|
* a set of loops pulling out image values and forming the avregae colour in 8
|
|
|
|
* it RGB for that particular screen pixel from the underlying image pixels,
|
|
|
|
* no matter what format they are in.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
_create_image_row(struct _bitmap_decompose *img)
|
|
|
|
{
|
|
|
|
if(img->cur_screen_row >= img->screen_h)
|
|
|
|
{
|
|
|
|
NSLog(@"Tried to create too many screen rows");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(img->is_direct_packed)
|
|
|
|
/* do direct copy, only limited formats supported */
|
|
|
|
{
|
|
|
|
unsigned char *ptr = img->plane[0];
|
|
|
|
unsigned char *rptr = img->r;
|
|
|
|
unsigned char *gptr = img->g;
|
|
|
|
unsigned char *bptr = img->b;
|
|
|
|
unsigned char *aptr = img->a;
|
|
|
|
BOOL oib = img->one_is_black;
|
|
|
|
BOOL ha = img->has_alpha;
|
|
|
|
BOOL is_grey = (img->cspace == gray_colorspace);
|
|
|
|
int i, fc = img->first_vis_col, lc = img->last_vis_col;
|
|
|
|
|
|
|
|
/* do the offset from the right */
|
|
|
|
ptr += fc * ( (is_grey ? 1 : 3) + (ha ? 1 : 0) );
|
|
|
|
rptr += fc;
|
|
|
|
gptr += fc;
|
|
|
|
bptr += fc;
|
|
|
|
aptr += fc;
|
|
|
|
|
|
|
|
for(i = fc;i <= lc; i++)
|
|
|
|
{
|
|
|
|
*rptr = *ptr++;
|
|
|
|
if(is_grey)
|
|
|
|
{
|
|
|
|
if(oib)
|
|
|
|
*rptr = 255 - *rptr;
|
|
|
|
*gptr = *bptr = *rptr;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*gptr = *ptr++;
|
|
|
|
*bptr = *ptr++;
|
|
|
|
}
|
|
|
|
*aptr = ha ? *ptr++ : 255; /* opaque default */
|
|
|
|
|
|
|
|
rptr++;
|
|
|
|
gptr++;
|
|
|
|
bptr++;
|
|
|
|
aptr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
img->plane[0] += img->bpr;
|
|
|
|
img->cur_image_row++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
/* do the full averaging row creation */
|
|
|
|
{
|
|
|
|
int tr, sc;
|
|
|
|
int s_row = img->row_starts[img->cur_screen_row];
|
|
|
|
int e_row = img->row_ends[img->cur_screen_row];
|
|
|
|
BOOL zero_count = YES;
|
|
|
|
|
|
|
|
/* local copies of stuff we use a lot to avoid indirect */
|
|
|
|
unsigned char **plane = img->plane;
|
|
|
|
unsigned int *bit_off = img->bit_off;
|
|
|
|
unsigned int *col_starts = img->col_starts;
|
|
|
|
unsigned int *col_ends = img->col_ends;
|
|
|
|
unsigned int *r_sum = img->r_sum;
|
|
|
|
unsigned int *g_sum = img->g_sum;
|
|
|
|
unsigned int *b_sum = img->b_sum;
|
|
|
|
unsigned int *a_sum = img->a_sum;
|
|
|
|
unsigned int *pix_count = img->pix_count;
|
|
|
|
unsigned char *rptr = img->r;
|
|
|
|
unsigned char *gptr = img->g;
|
|
|
|
unsigned char *bptr = img->b;
|
|
|
|
unsigned char *aptr = img->a;
|
|
|
|
int spp = img->spp;
|
|
|
|
int bpp = img->bpp;
|
|
|
|
int bps = img->bps;
|
|
|
|
int bpr = img->bpr;
|
|
|
|
int pro_mul = img->pro_mul;
|
|
|
|
int cspace = img->cspace;
|
|
|
|
BOOL ha = img->has_alpha;
|
|
|
|
BOOL oib = img->one_is_black;
|
|
|
|
int fc = img->first_vis_col, lc = img->last_vis_col;
|
|
|
|
|
|
|
|
|
|
|
|
/* loop for each required row */
|
|
|
|
zero_count = YES;
|
|
|
|
for(tr = s_row; tr <= e_row; tr++)
|
|
|
|
{
|
|
|
|
/* move to the required image row */
|
|
|
|
while(img->cur_image_row < tr)
|
|
|
|
{
|
|
|
|
int p;
|
|
|
|
for(p = 0; p < spp; p++)
|
|
|
|
plane[p] += bpr;
|
|
|
|
img->cur_image_row++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* for each screen pixel */
|
|
|
|
for(sc = fc; sc <= lc; sc++)
|
|
|
|
{
|
|
|
|
int s_col = col_starts[sc];
|
|
|
|
int e_col = col_ends[sc];
|
|
|
|
int tc;
|
|
|
|
|
|
|
|
if(zero_count)
|
|
|
|
{
|
|
|
|
r_sum[sc] = 0;
|
|
|
|
g_sum[sc] = 0;
|
|
|
|
b_sum[sc] = 0;
|
|
|
|
a_sum[sc] = 0;
|
|
|
|
pix_count[sc] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* for each image pixel */
|
|
|
|
for(tc = s_col; tc <= e_col; tc++)
|
|
|
|
{
|
|
|
|
unsigned char r, g, b, a;
|
|
|
|
_get_image_pixel(tc, &r, &g, &b, &a,
|
|
|
|
plane, bit_off, spp, bpp, bps,
|
|
|
|
pro_mul, cspace, ha, oib);
|
|
|
|
r_sum[sc] += r;
|
|
|
|
g_sum[sc] += g;
|
|
|
|
b_sum[sc] += b;
|
|
|
|
a_sum[sc] += a;
|
|
|
|
pix_count[sc]++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
zero_count =NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Build the line by averaging. As integer divide always
|
|
|
|
* rounds down we can get the effect of adding 0.5 before
|
|
|
|
* trucating by adding the value of the count right shifted
|
|
|
|
* one bit, thus giving us the nearest value and therefore a
|
|
|
|
* better approximation t the colour we hope.
|
|
|
|
*/
|
|
|
|
for(sc = fc; sc <= lc; sc++)
|
|
|
|
{
|
|
|
|
int count = pix_count[sc];
|
|
|
|
int half = count >> 1;
|
|
|
|
rptr[sc] = (r_sum[sc] + half) / count;
|
|
|
|
gptr[sc] = (g_sum[sc] + half) / count;
|
|
|
|
bptr[sc] = (b_sum[sc] + half) / count;
|
|
|
|
aptr[sc] = (a_sum[sc] + half) / count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
img->cur_screen_row++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the ranges covered by a pixel within the image. Given the source
|
|
|
|
* number of pixels and the destination number of pixels we calculate which
|
|
|
|
* pixels in the source are more than 50% overlapped by each pixel in the
|
|
|
|
* destination and record the start and end of the range. For mappings where
|
|
|
|
* the source is being magnified this will only be a single pixel, for others
|
|
|
|
* it may be one or more pixels, spaced evenly along the line. These are
|
|
|
|
* the pixels which will then be averaged to make the best guess colour for
|
|
|
|
* the destination pixel. As this is a slow process then a flag is passed in
|
|
|
|
* which will cause the nearest pixel alorithm that is used for magnification
|
|
|
|
* to be applied to minificationas well. The result looks rougher, but is much
|
|
|
|
* faster and proprtional to the size of the output rather than the input
|
|
|
|
* image.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
_set_ranges(int src_len, int dst_len,
|
|
|
|
int *start_ptr, int *end_ptr, BOOL fast_min)
|
|
|
|
{
|
|
|
|
float dst_f = (float)dst_len;
|
|
|
|
int d;
|
|
|
|
if(fast_min || (src_len <= dst_len))
|
|
|
|
/* magnifying */
|
|
|
|
{
|
|
|
|
float src_f = (float)src_len;
|
|
|
|
for(d = 0; d < dst_len; d++)
|
|
|
|
{
|
|
|
|
int middle = (int)((((float)d + 0.5) * src_f) / dst_f);
|
|
|
|
*start_ptr++ = middle;
|
|
|
|
*end_ptr++ = middle;
|
|
|
|
if(middle >= src_len)
|
|
|
|
NSLog(@"Problem with magnification!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int start = 0;
|
|
|
|
for(d = 0; d < dst_len; d++)
|
|
|
|
{
|
|
|
|
int end_i = (int)(0.5 + (((d+1) * src_len) / dst_f));
|
|
|
|
if((end_i > src_len) || (end_i < 1))
|
|
|
|
NSLog(@"Problem with minification!");
|
|
|
|
*start_ptr++ = start;
|
|
|
|
*end_ptr++ = end_i - 1;
|
|
|
|
start = end_i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Combine the image data with the currentonscreen data and push the
|
|
|
|
* result back to the screen. The screen handling code is almost identical
|
|
|
|
* to the original code. The function assumes that the displayable rectangle
|
|
|
|
* is always a subset of the complete screen rectangle which is the area
|
|
|
|
* in pixels that would be covered by the entire image. This is used to
|
|
|
|
* calculate the scaling required.
|
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
|
|
|
_bitmap_combine_alpha(RContext *context,
|
|
|
|
unsigned char * data_planes[5],
|
|
|
|
int width, int height,
|
|
|
|
int bits_per_sample, int samples_per_pixel,
|
|
|
|
int bits_per_pixel, int bytes_per_row,
|
|
|
|
int colour_space, BOOL one_is_black,
|
|
|
|
BOOL is_planar, BOOL has_alpha, BOOL fast_min,
|
|
|
|
RXImage *dest_im, RXImage *dest_alpha,
|
|
|
|
XRectangle srect, XRectangle drect,
|
|
|
|
NSCompositingOperation op,
|
|
|
|
XGDrawMechanism drawMechanism)
|
|
|
|
{
|
|
|
|
struct _bitmap_decompose img;
|
|
|
|
XColor c0;
|
|
|
|
XColor c1;
|
|
|
|
int col_shift = drect.x - srect.x;
|
|
|
|
int row_shift = drect.y - srect.y;
|
|
|
|
|
|
|
|
/* Sanity check on colourspace and number of colours */
|
|
|
|
{
|
|
|
|
int num_of_colours = samples_per_pixel - (has_alpha ? 1 : 0);
|
|
|
|
switch(colour_space)
|
|
|
|
{
|
|
|
|
case hsb_colorspace:
|
|
|
|
NSLog(@"HSB colourspace not supported for images");
|
|
|
|
return -1;
|
|
|
|
case rgb_colorspace:
|
|
|
|
if(num_of_colours != 3)
|
|
|
|
{
|
|
|
|
NSLog(@"Bad number of colour planes - d", num_of_colours);
|
|
|
|
NSLog(@"RGB colourspace requires three planes excluding alpha");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case cmyk_colorspace:
|
|
|
|
if(num_of_colours != 4)
|
|
|
|
{
|
|
|
|
NSLog(@"Bad number of colour planes - %d", num_of_colours);
|
|
|
|
NSLog(@"CMYK colourspace requires four planes excluding alpha");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case gray_colorspace:
|
|
|
|
if(num_of_colours != 1)
|
|
|
|
{
|
|
|
|
NSLog(@"Bad number of colour planes - %d", num_of_colours);
|
|
|
|
NSLog(@"Gray colourspace requires one plane excluding alpha");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
NSLog(@"Unknown colourspace found");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* bitmap decomposition structure */
|
|
|
|
img.bps = bits_per_sample;
|
|
|
|
img.bpp = bits_per_pixel;
|
|
|
|
img.spp = samples_per_pixel;
|
|
|
|
img.bpr = bytes_per_row;
|
|
|
|
img.image_w = width;
|
|
|
|
img.image_h = height;
|
|
|
|
img.screen_w = srect.width;
|
|
|
|
img.screen_h = srect.height;
|
|
|
|
img.has_alpha = has_alpha;
|
|
|
|
img.one_is_black = one_is_black;
|
|
|
|
img.cspace = colour_space;
|
|
|
|
img.cur_image_row = 0;
|
|
|
|
img.cur_screen_row = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Promotion value, this is what the samples need to
|
|
|
|
* be mutiplied by to get an 8 bit value. This is done
|
|
|
|
* rather than shifting to fill in the lower bits properly.
|
|
|
|
* The values for 6, 5 and 3 bits shouldbe floats by rights,
|
|
|
|
* but the difference is negligble and for speed we want to use
|
|
|
|
* integers.
|
|
|
|
*/
|
|
|
|
switch(bits_per_sample)
|
|
|
|
{
|
|
|
|
case 8:
|
|
|
|
img.pro_mul = 1;
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
img.pro_mul = 2;
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
img.pro_mul = 4; /* should be 4.05 */
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
img.pro_mul = 8; /* should be 8.226 */
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
img.pro_mul = 17;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
img.pro_mul = 36; /* should be 36.43 */
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
img.pro_mul = 85;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
img.pro_mul = 255;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
NSLog(@"Bizzare number of bits per sample %d", bits_per_sample);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle planar or meshed data. We hold an array of
|
|
|
|
* base addresses and bit offsets for all formats.
|
|
|
|
* Thus for meshed data the bases are the same with
|
|
|
|
* increasing bit offsets, but for planar data the bases
|
|
|
|
* are those of the planes, with the bit offset always being
|
|
|
|
* zero.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* zero them */
|
|
|
|
for(i=0;i<5;i++)
|
|
|
|
{
|
|
|
|
img.plane[i] = NULL;
|
|
|
|
img.bit_off[i] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set as appropriate */
|
|
|
|
if(is_planar)
|
|
|
|
for(i=0;i<img.spp;i++)
|
|
|
|
img.plane[i] = data_planes[i];
|
|
|
|
else
|
|
|
|
for(i=0;i<img.spp;i++)
|
|
|
|
{
|
|
|
|
img.plane[i] = data_planes[0];
|
|
|
|
img.bit_off[i] = i * img.bps;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the range mappings. If the data can be copied at 1:1
|
|
|
|
* then a flag is set to allow us to do this quickly. Else a set
|
|
|
|
* of ranges are produced. Each of these correspons to the pixels
|
|
|
|
* within the image that are behind each point on the screen. Rows
|
|
|
|
* are then composed by averaging all the image points behind a screen
|
|
|
|
* pixel to give the illusion of smooth minification. For a magnification
|
|
|
|
* the range boundaries are equal and thus a "nearest" function is
|
|
|
|
* produced such that the image pixels become visible as it is enlarged.
|
|
|
|
* As the calculation of the ranges requires floats we do it once here
|
|
|
|
* and then re-use the values calculated on each row to avoid excessive
|
|
|
|
* floating point calculation.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if(!is_planar && (img.screen_w == img.image_w)
|
|
|
|
&& (img.screen_h == img.image_h) && (img.bps == 8)
|
|
|
|
&& ((img.cspace == gray_colorspace) || (img.cspace == rgb_colorspace))
|
|
|
|
&& ((img.bpr * 8) == (img.bps * img.spp * img.image_w)))
|
|
|
|
{
|
|
|
|
img.is_direct_packed = YES;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
img.is_direct_packed = NO;
|
|
|
|
|
|
|
|
img.row_starts = malloc(img.screen_h * sizeof(int));
|
|
|
|
img.row_ends = malloc(img.screen_h * sizeof(int));
|
|
|
|
_set_ranges(img.image_h, img.screen_h,
|
|
|
|
img.row_starts, img.row_ends, fast_min);
|
|
|
|
|
|
|
|
img.col_starts = malloc(img.screen_w * sizeof(int));
|
|
|
|
img.col_ends = malloc(img.screen_w * sizeof(int));
|
|
|
|
_set_ranges(img.image_w, img.screen_w,
|
|
|
|
img.col_starts, img.col_ends, fast_min);
|
|
|
|
|
|
|
|
img.r_sum = malloc(img.screen_w * sizeof(int));
|
|
|
|
img.g_sum = malloc(img.screen_w * sizeof(int));
|
|
|
|
img.b_sum = malloc(img.screen_w * sizeof(int));
|
|
|
|
img.a_sum = malloc(img.screen_w * sizeof(int));
|
|
|
|
img.pix_count = malloc(img.screen_w * sizeof(int));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* skip the top rows if a shift is needed */
|
|
|
|
if(row_shift)
|
|
|
|
{
|
|
|
|
if(img.is_direct_packed) /* need to move data */
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for(i=0;i<img.spp;i++)
|
|
|
|
img.plane[i] += row_shift * img.bpr;
|
|
|
|
}
|
|
|
|
img.cur_screen_row = row_shift;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set the visible segment of the row */
|
|
|
|
img.first_vis_col = col_shift;
|
|
|
|
img.last_vis_col = img.first_vis_col + drect.width -1;
|
|
|
|
|
|
|
|
/* make space for the row buffers */
|
|
|
|
img.r = malloc(img.screen_w);
|
|
|
|
img.g = malloc(img.screen_w);
|
|
|
|
img.b = malloc(img.screen_w);
|
|
|
|
img.a = malloc(img.screen_w);
|
|
|
|
|
|
|
|
{
|
2002-08-03 03:32:19 +00:00
|
|
|
unsigned long pixel;
|
2002-03-27 23:44:41 +00:00
|
|
|
|
|
|
|
/* Two cases, the *_FAST* method, which
|
|
|
|
is covered in the first a part of the if
|
|
|
|
clause.
|
|
|
|
When no FAST method is available (or recognized)
|
|
|
|
use the `official' X mechanism. This is
|
|
|
|
covered in the else clause */
|
|
|
|
|
|
|
|
if (drawMechanism == XGDM_FAST15
|
|
|
|
|| drawMechanism == XGDM_FAST16
|
|
|
|
|| drawMechanism == XGDM_FAST32
|
|
|
|
|| drawMechanism == XGDM_FAST32_BGR)
|
|
|
|
{
|
|
|
|
VARIABLES_DECLARATION;
|
|
|
|
unsigned row;
|
|
|
|
|
|
|
|
switch (drawMechanism)
|
|
|
|
{
|
|
|
|
case XGDM_FAST15:
|
|
|
|
InitRGBShiftsAndMasks(10,5,5,5,0,5,0,8);
|
|
|
|
break;
|
|
|
|
case XGDM_FAST16:
|
|
|
|
InitRGBShiftsAndMasks(11,5,5,6,0,5,0,8);
|
|
|
|
break;
|
|
|
|
case XGDM_FAST32:
|
|
|
|
InitRGBShiftsAndMasks(16,8,8,8,0,8,0,8);
|
|
|
|
break;
|
|
|
|
case XGDM_FAST32_BGR:
|
|
|
|
InitRGBShiftsAndMasks(0,8,8,8,16,8,0,8);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
NSLog(@"Huh? Backend confused about XGDrawMechanism");
|
|
|
|
//Try something. With a bit of luck we see
|
|
|
|
//which picture goes wrong.
|
|
|
|
InitRGBShiftsAndMasks(11,5,5,6,0,5,0,8);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (row = 0; row < drect.height; row++)
|
|
|
|
{
|
|
|
|
unsigned col;
|
|
|
|
unsigned char *rptr = img.r + col_shift;
|
|
|
|
unsigned char *gptr = img.g + col_shift;
|
|
|
|
unsigned char *bptr = img.b + col_shift;
|
|
|
|
unsigned char *aptr = img.a + col_shift;
|
|
|
|
|
|
|
|
_create_image_row(&img);
|
|
|
|
|
|
|
|
for (col = 0; col < drect.width; col++)
|
|
|
|
{
|
2002-07-15 02:50:59 +00:00
|
|
|
unsigned short sr = (*rptr++ >> (8 - _rwidth));
|
|
|
|
unsigned short sg = (*gptr++ >> (8 - _gwidth));
|
|
|
|
unsigned short sb = (*bptr++ >> (8 - _bwidth));
|
|
|
|
unsigned short sa = (*aptr++ >> (8 - _awidth));
|
2002-08-03 03:32:19 +00:00
|
|
|
unsigned dr, dg, db, da; // dest
|
2002-07-15 02:50:59 +00:00
|
|
|
|
|
|
|
if (sa == 0) // dest wouldn't be changed
|
|
|
|
continue;
|
2002-03-27 23:44:41 +00:00
|
|
|
|
2002-08-03 03:32:19 +00:00
|
|
|
if (sa == _amask) // source only, don't bother with the rest
|
2002-03-27 23:44:41 +00:00
|
|
|
{
|
2002-08-03 03:32:19 +00:00
|
|
|
// Yes, this is duplicated code -- but it's worth it.
|
|
|
|
RGBToPixel(sr, sg, sb, pixel);
|
|
|
|
XPutPixel(dest_im->image, col, row, pixel);
|
|
|
|
if (dest_alpha)
|
|
|
|
XPutPixel(dest_alpha->image, col, row, sa << _ashift);
|
|
|
|
continue;
|
2002-07-15 02:50:59 +00:00
|
|
|
}
|
2002-08-03 03:32:19 +00:00
|
|
|
|
2002-07-15 02:50:59 +00:00
|
|
|
// get the destination pixel
|
|
|
|
pixel = XGetPixel(dest_im->image, col, row);
|
|
|
|
PixelToRGB(pixel, dr, dg, db);
|
|
|
|
|
|
|
|
if (dest_alpha)
|
|
|
|
{
|
|
|
|
pixel = XGetPixel(dest_alpha->image, col, row);
|
|
|
|
da = (pixel >> _ashift) & _amask;
|
|
|
|
}
|
|
|
|
else // no alpha channel, background is opaque
|
|
|
|
da = _amask;
|
2002-08-03 03:32:19 +00:00
|
|
|
|
|
|
|
if (da == 0)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Unscale the colors
|
|
|
|
*/
|
|
|
|
dr = (sr * _amask) / sa;
|
|
|
|
dg = (sg * _amask) / sa;
|
|
|
|
db = (sb * _amask) / sa;
|
2002-07-15 02:50:59 +00:00
|
|
|
da = sa;
|
2002-03-27 23:44:41 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2002-08-03 03:32:19 +00:00
|
|
|
unsigned ialpha = _amask - sa;
|
|
|
|
|
|
|
|
dr = (sr * sa + (dr * ialpha)) / _amask;
|
|
|
|
dg = (sg * sa + (dg * ialpha)) / _amask;
|
|
|
|
db = (sb * sa + (db * ialpha)) / _amask;
|
2002-07-15 02:50:59 +00:00
|
|
|
if (da == _amask || da == _amask)
|
|
|
|
da = _amask;
|
|
|
|
else
|
2002-08-03 03:32:19 +00:00
|
|
|
da = sa + ((da * ialpha) / _amask);
|
2002-03-27 23:44:41 +00:00
|
|
|
}
|
|
|
|
|
2002-07-15 02:50:59 +00:00
|
|
|
CLAMP(dr);
|
|
|
|
CLAMP(dg);
|
|
|
|
CLAMP(db);
|
|
|
|
CLAMP(da);
|
|
|
|
|
|
|
|
RGBToPixel(dr, dg, db, pixel);
|
2002-03-27 23:44:41 +00:00
|
|
|
XPutPixel(dest_im->image, col, row, pixel);
|
2002-07-15 02:50:59 +00:00
|
|
|
if (dest_alpha)
|
|
|
|
XPutPixel(dest_alpha->image, col, row, da << _ashift);
|
2002-03-27 23:44:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2003-11-20 16:29:32 +00:00
|
|
|
XColor c2, a2;
|
|
|
|
RColor rc, rc2;
|
|
|
|
unsigned row, oldAlpha = 65537;
|
2003-10-20 03:45:55 +00:00
|
|
|
unsigned long pixels[CSIZE];
|
|
|
|
XColor colors[CSIZE];
|
|
|
|
BOOL empty[CSIZE];
|
|
|
|
int cind;
|
|
|
|
|
|
|
|
for(cind = 0; cind < CSIZE; cind++)
|
|
|
|
{
|
|
|
|
empty[cind] = YES;
|
|
|
|
}
|
2002-03-27 23:44:41 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This block of code should be totally portable as it uses the
|
|
|
|
* 'official' X mechanism for converting from pixel values to
|
|
|
|
* RGB color values - on the downside, it's very slow.
|
|
|
|
*/
|
|
|
|
pixel = (unsigned long)-1; // Never valid?
|
2003-11-20 16:29:32 +00:00
|
|
|
c1.pixel = c2.pixel = a2.pixel = pixel;
|
2002-03-27 23:44:41 +00:00
|
|
|
|
|
|
|
for (row = 0; row < drect.height; row++)
|
|
|
|
{
|
|
|
|
unsigned col;
|
|
|
|
unsigned char *rptr = img.r + col_shift;
|
|
|
|
unsigned char *gptr = img.g + col_shift;
|
|
|
|
unsigned char *bptr = img.b + col_shift;
|
|
|
|
unsigned char *aptr = img.a + col_shift;
|
|
|
|
|
|
|
|
_create_image_row(&img);
|
|
|
|
|
|
|
|
for (col = 0; col < drect.width; col++)
|
|
|
|
{
|
|
|
|
unsigned short r = *rptr++;
|
|
|
|
unsigned short g = *gptr++;
|
|
|
|
unsigned short b = *bptr++;
|
|
|
|
unsigned short alpha = *aptr++;;
|
|
|
|
|
|
|
|
if (has_alpha)
|
|
|
|
{
|
|
|
|
if (alpha != oldAlpha)
|
|
|
|
{
|
|
|
|
oldAlpha = alpha;
|
2003-11-20 16:29:32 +00:00
|
|
|
rc.red = rc.green = rc.blue = alpha;
|
|
|
|
RGetClosestXColor(context, &rc, &a2);
|
2002-03-27 23:44:41 +00:00
|
|
|
}
|
|
|
|
if (dest_alpha)
|
|
|
|
{
|
|
|
|
XColor da;
|
|
|
|
/* Alpha gets mixed the same as all the
|
|
|
|
other color components */
|
|
|
|
da.pixel = XGetPixel(dest_alpha->image, col, row);
|
2003-10-20 03:45:55 +00:00
|
|
|
GS_QUERY_COLOR(da);
|
2003-11-20 16:29:32 +00:00
|
|
|
rc.red = alpha + (da.red >> 8) * (256 - alpha)/256;
|
|
|
|
rc.green = rc.blue = rc.red;
|
|
|
|
RGetClosestXColor(context, &rc, &da);
|
2002-03-27 23:44:41 +00:00
|
|
|
XPutPixel(dest_alpha->image, col, row, da.pixel);
|
|
|
|
}
|
|
|
|
if (alpha == 0)
|
|
|
|
continue; // background unchanged.
|
|
|
|
|
|
|
|
if (alpha == 255)
|
|
|
|
{
|
2003-11-20 16:29:32 +00:00
|
|
|
rc.red = r;
|
|
|
|
rc.green = g;
|
|
|
|
rc.blue = b;
|
2002-03-27 23:44:41 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
unsigned short ialpha = 255 - alpha;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* RGB color components in X are 16-bit values -
|
|
|
|
* but our bitmap contains 8-bit values so we must
|
|
|
|
* adjust our values up to 16-bit, which we can do
|
|
|
|
* by increasing their alpha component by 256.
|
|
|
|
*/
|
|
|
|
alpha <<= 8;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the background pixel and convert to RGB if
|
|
|
|
* we haven't already done the conversion earlier.
|
|
|
|
*/
|
|
|
|
c0.pixel = XGetPixel(dest_im->image, col, row);
|
2003-11-20 16:29:32 +00:00
|
|
|
if (c0.pixel != c2.pixel)
|
2002-03-27 23:44:41 +00:00
|
|
|
{
|
2003-11-20 16:29:32 +00:00
|
|
|
c2.pixel = c0.pixel;
|
2003-10-20 03:45:55 +00:00
|
|
|
GS_QUERY_COLOR(c0);
|
2002-03-27 23:44:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// mix in alpha to produce RGB out
|
2003-11-20 16:29:32 +00:00
|
|
|
rc.red = ((c0.red*ialpha) + (r*alpha))/(255*256);
|
|
|
|
rc.green = ((c0.green*ialpha) + (g*alpha))/(255*256);
|
|
|
|
rc.blue = ((c0.blue*ialpha) + (b*alpha))/(255*256);
|
2002-03-27 23:44:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Not using alpha, but we still have to set it
|
|
|
|
in the pixmap */
|
|
|
|
if (a2.pixel == pixel)
|
|
|
|
{
|
2003-11-20 16:29:32 +00:00
|
|
|
rc.red = rc.green = rc.blue = 255;
|
|
|
|
RGetClosestXColor(context, &rc, &a2);
|
2002-03-27 23:44:41 +00:00
|
|
|
}
|
|
|
|
if (dest_alpha)
|
|
|
|
XPutPixel(dest_alpha->image, col, row, a2.pixel);
|
2003-11-20 16:29:32 +00:00
|
|
|
rc.red = r;
|
|
|
|
rc.green = g;
|
|
|
|
rc.blue = b;
|
2002-03-27 23:44:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert the X RGB value to a colormap entry if we
|
|
|
|
* don't already have one. Then set the pixel.
|
2003-11-20 16:29:32 +00:00
|
|
|
* NB. We will not necessarily get a
|
2002-03-27 23:44:41 +00:00
|
|
|
* color with exactly the rgb components we gave it, so
|
|
|
|
* we must record those components beforehand.
|
|
|
|
*/
|
2003-11-20 16:29:32 +00:00
|
|
|
if (c1.pixel == pixel
|
|
|
|
|| rc.red != rc2.red
|
|
|
|
|| rc.green != rc2.green
|
|
|
|
|| rc.blue != rc2.blue)
|
2002-03-27 23:44:41 +00:00
|
|
|
{
|
2003-11-20 16:29:32 +00:00
|
|
|
rc2 = rc;
|
|
|
|
RGetClosestXColor(context, &rc, &c1);
|
2002-03-27 23:44:41 +00:00
|
|
|
}
|
|
|
|
XPutPixel(dest_im->image, col, row, c1.pixel);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free used memory */
|
|
|
|
free(img.r);
|
|
|
|
free(img.g);
|
|
|
|
free(img.b);
|
|
|
|
free(img.a);
|
|
|
|
if(!img.is_direct_packed)
|
|
|
|
{
|
|
|
|
free(img.row_starts);
|
|
|
|
free(img.row_ends);
|
|
|
|
free(img.col_starts);
|
|
|
|
free(img.col_ends);
|
|
|
|
free(img.r_sum);
|
|
|
|
free(img.g_sum);
|
|
|
|
free(img.b_sum);
|
|
|
|
free(img.a_sum);
|
|
|
|
free(img.pix_count);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2003-09-21 03:19:17 +00:00
|
|
|
|
|
|
|
NSData *
|
|
|
|
_pixmap_read_alpha(RContext *context,
|
|
|
|
RXImage *source_im, RXImage *source_alpha,
|
|
|
|
XRectangle srect,
|
|
|
|
XGDrawMechanism drawMechanism)
|
|
|
|
{
|
|
|
|
unsigned long pixel;
|
|
|
|
NSMutableData *data;
|
|
|
|
unsigned char *bytes;
|
|
|
|
int spp;
|
|
|
|
|
|
|
|
spp = (source_alpha) ? 4 : 3;
|
|
|
|
data = [NSMutableData dataWithLength: srect.width*srect.height*spp];
|
|
|
|
if (data == nil)
|
|
|
|
return nil;
|
|
|
|
bytes = [data mutableBytes];
|
|
|
|
|
|
|
|
if (drawMechanism == XGDM_FAST15
|
|
|
|
|| drawMechanism == XGDM_FAST16
|
|
|
|
|| drawMechanism == XGDM_FAST32
|
|
|
|
|| drawMechanism == XGDM_FAST32_BGR)
|
|
|
|
{
|
|
|
|
VARIABLES_DECLARATION;
|
|
|
|
unsigned row;
|
|
|
|
|
|
|
|
switch (drawMechanism)
|
|
|
|
{
|
|
|
|
case XGDM_FAST15:
|
|
|
|
InitRGBShiftsAndMasks(10,5,5,5,0,5,0,8);
|
|
|
|
break;
|
|
|
|
case XGDM_FAST16:
|
|
|
|
InitRGBShiftsAndMasks(11,5,5,6,0,5,0,8);
|
|
|
|
break;
|
|
|
|
case XGDM_FAST32:
|
|
|
|
InitRGBShiftsAndMasks(16,8,8,8,0,8,0,8);
|
|
|
|
break;
|
|
|
|
case XGDM_FAST32_BGR:
|
|
|
|
InitRGBShiftsAndMasks(0,8,8,8,16,8,0,8);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
NSLog(@"Huh? Backend confused about XGDrawMechanism");
|
|
|
|
//Try something. With a bit of luck we see
|
|
|
|
//which picture goes wrong.
|
|
|
|
InitRGBShiftsAndMasks(11,5,5,6,0,5,0,8);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (row = 0; row < srect.height; row++)
|
|
|
|
{
|
|
|
|
unsigned col;
|
|
|
|
|
|
|
|
for (col = 0; col < srect.width; col++)
|
|
|
|
{
|
|
|
|
unsigned sr, sg, sb, sa;
|
|
|
|
|
|
|
|
// Get the source pixel information
|
|
|
|
pixel = XGetPixel(source_im->image, col, row);
|
|
|
|
PixelToRGB(pixel, sr, sg, sb);
|
|
|
|
// Expand to 8 bit value
|
|
|
|
sr = (sr << (8-_rwidth));
|
|
|
|
sg = (sg << (8-_gwidth));
|
|
|
|
sb = (sb << (8-_bwidth));
|
|
|
|
|
|
|
|
if (source_alpha)
|
|
|
|
{
|
|
|
|
pixel = XGetPixel(source_alpha->image, col, row);
|
|
|
|
sa = (pixel >> _ashift) & _amask;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
sa = _amask;
|
|
|
|
|
|
|
|
bytes[(row * srect.width + col)*spp] = sr;
|
|
|
|
bytes[(row * srect.width + col)*spp+1] = sg;
|
|
|
|
bytes[(row * srect.width + col)*spp+2] = sb;
|
|
|
|
if (source_alpha)
|
|
|
|
bytes[(row * srect.width + col)*spp+3] = sa;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
XColor c2;
|
|
|
|
unsigned row;
|
2003-10-20 03:45:55 +00:00
|
|
|
unsigned long pixels[CSIZE];
|
|
|
|
XColor colors[CSIZE];
|
|
|
|
BOOL empty[CSIZE];
|
|
|
|
int cind;
|
|
|
|
|
|
|
|
for(cind = 0; cind < CSIZE; cind++)
|
|
|
|
{
|
|
|
|
empty[cind] = YES;
|
|
|
|
}
|
2003-09-21 03:19:17 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This block of code should be totally portable as it uses the
|
|
|
|
* 'official' X mechanism for converting from pixel values to
|
|
|
|
* RGB color values - on the downside, it's very slow.
|
|
|
|
*/
|
|
|
|
pixel = (unsigned long)-1; // Never valid?
|
|
|
|
c2.pixel = pixel;
|
|
|
|
|
|
|
|
for (row = 0; row < srect.height; row++)
|
|
|
|
{
|
|
|
|
unsigned col;
|
|
|
|
|
|
|
|
for (col = 0; col < srect.width; col++)
|
|
|
|
{
|
|
|
|
int r, g, b, alpha;
|
|
|
|
XColor pcolor, acolor;
|
|
|
|
pcolor.pixel = XGetPixel(source_im->image, col, row);
|
2003-10-20 03:45:55 +00:00
|
|
|
GS_QUERY_COLOR(pcolor);
|
2003-09-21 03:19:17 +00:00
|
|
|
r = pcolor.red >> 8;
|
|
|
|
g = pcolor.green >> 8;
|
|
|
|
b = pcolor.blue >> 8;
|
|
|
|
alpha = 255;
|
|
|
|
if (source_alpha)
|
|
|
|
{
|
|
|
|
acolor.pixel = XGetPixel(source_alpha->image, col, row);
|
2003-10-20 03:45:55 +00:00
|
|
|
GS_QUERY_COLOR(acolor);
|
2003-09-21 03:19:17 +00:00
|
|
|
alpha = acolor.red >> 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
bytes[(row * srect.width + col)*spp] = r;
|
|
|
|
bytes[(row * srect.width + col)*spp+1] = g;
|
|
|
|
bytes[(row * srect.width + col)*spp+2] = b;
|
|
|
|
if (source_alpha)
|
|
|
|
bytes[(row * srect.width + col)*spp+3] = alpha;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (NSData *)data;
|
|
|
|
}
|
|
|
|
|