libs-back/Source/xlib/XGBitmap.m
Adrian Robert 106c7e3674 new default 'GSModifiersAreKeys', if set XGServerEvent will always interpret the same key as the same keysym/modifier; also, fix typo in xlib/XGBitmap error message
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/back/trunk@20297 72102866-910b-0410-8b05-ffd578937521
2004-11-05 04:31:26 +00:00

1313 lines
35 KiB
Objective-C

/*
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>
#include <Foundation/NSData.h>
#include <Foundation/NSDebug.h>
#include "gsc/gscolors.h"
#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 { \
_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); \
} while(0)
#define PixelToRGB(pixel,r,g,b) \
do { \
(r) = (pixel >> _rshift) & _rmask; \
(g) = (pixel >> _gshift) & _gmask; \
(b) = (pixel >> _bshift) & _bmask; \
} 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)
#define CLAMP(a) \
do { \
(a) = MAX(0, MIN(255, (a))); \
} while (0)
#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)
/* 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;
fraction = MAX(0.0, MIN(1.0, fraction));
if (drawMechanism == XGDM_FAST15
|| drawMechanism == XGDM_FAST16
|| drawMechanism == XGDM_FAST32
|| drawMechanism == XGDM_FAST32_BGR
|| drawMechanism == XGDM_FAST8)
{
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;
case XGDM_FAST8:
InitRGBShiftsAndMasks(5,3,2,3,0,2,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; // source
unsigned dr, dg, db, da; // dest
unsigned ialpha;
// Get the source pixel information
pixel = XGetPixel(source_im->image, srect.x+col, srect.y+row);
PixelToRGB(pixel, sr, sg, sb);
if (source_alpha)
{
pixel = XGetPixel(source_alpha->image,
srect.x + col, srect.y + row);
sa = (pixel >> _ashift) & _amask;
}
else
sa = _amask;
if (sa == 0) // dest wouldn't be changed
continue;
if (fraction < 1.0)
sa *= fraction;
sr *= sa;
sg *= sa;
sb *= sa;
ialpha = (_amask - sa);
// Now get dest pixel
pixel = XGetPixel(dest_im->image, col, row);
PixelToRGB(pixel, dr, dg, db);
dr *= ialpha;
dg *= ialpha;
db *= ialpha;
if (dest_alpha)
{
pixel = XGetPixel(dest_alpha->image, col, row);
da = (pixel >> _ashift) & _amask;
}
else // no alpha channel, background is opaque
da = _amask;
dr = (sr + dr) / _amask;
dg = (sg + dg) / _amask;
db = (sb + db) / _amask;
// calc final alpha
if (sa == _amask || da == _amask)
da = _amask;
else
da = sa + ((da * ialpha) / _amask);
CLAMP(dr);
CLAMP(dg);
CLAMP(db);
CLAMP(da);
RGBToPixel(dr, dg, db, pixel);
XPutPixel(dest_im->image, col, row, pixel);
if (dest_alpha)
XPutPixel(dest_alpha->image, col, row, da << _ashift);
}
}
}
else
{
unsigned long pixels[CSIZE];
XColor colors[CSIZE];
BOOL empty[CSIZE];
XColor c2;
unsigned row;
int cind;
for(cind = 0; cind < CSIZE; cind++)
{
empty[cind] = YES;
}
/*
* 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);
GS_QUERY_COLOR(pcolor);
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);
GS_QUERY_COLOR(acolor);
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;
GS_QUERY_COLOR(c0);
}
// 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.
* NB. We will not necessarily get a
* 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)
{
RColor rc;
c2 = c1;
rc.red = c1.red >> 8;
rc.green = c1.green >> 8;
rc.blue = c1.blue >> 8;
RGetClosestXColor(context, &rc, &c1);
c2.pixel = c1.pixel;
}
XPutPixel(dest_im->image, col, row, c1.pixel);
if (dest_alpha)
{
RColor rc;
XColor da;
/* Alpha gets mixed the same as all the
other color components */
da.pixel = XGetPixel(dest_alpha->image, col, row);
GS_QUERY_COLOR(da);
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);
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);
{
unsigned long pixel;
/* 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
|| drawMechanism == XGDM_FAST8)
{
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;
case XGDM_FAST8:
InitRGBShiftsAndMasks(5,3,2,3,0,2,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++)
{
unsigned short sr = (*rptr++ >> (8 - _rwidth));
unsigned short sg = (*gptr++ >> (8 - _gwidth));
unsigned short sb = (*bptr++ >> (8 - _bwidth));
unsigned short sa = (*aptr++ >> (8 - _awidth));
unsigned dr, dg, db, da; // dest
if (sa == 0) // dest wouldn't be changed
continue;
if (sa == _amask) // source only, don't bother with the rest
{
// 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;
}
// 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;
if (da == 0)
{
/*
* Unscale the colors
*/
dr = (sr * _amask) / sa;
dg = (sg * _amask) / sa;
db = (sb * _amask) / sa;
da = sa;
}
else
{
unsigned ialpha = _amask - sa;
dr = (sr * sa + (dr * ialpha)) / _amask;
dg = (sg * sa + (dg * ialpha)) / _amask;
db = (sb * sa + (db * ialpha)) / _amask;
if (da == _amask || da == _amask)
da = _amask;
else
da = sa + ((da * ialpha) / _amask);
}
CLAMP(dr);
CLAMP(dg);
CLAMP(db);
CLAMP(da);
RGBToPixel(dr, dg, db, pixel);
XPutPixel(dest_im->image, col, row, pixel);
if (dest_alpha)
XPutPixel(dest_alpha->image, col, row, da << _ashift);
}
}
}
else
{
XColor c2, a2;
RColor rc, rc2;
unsigned row, oldAlpha = 65537;
unsigned long pixels[CSIZE];
XColor colors[CSIZE];
BOOL empty[CSIZE];
int cind;
for(cind = 0; cind < CSIZE; cind++)
{
empty[cind] = YES;
}
/*
* 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?
c1.pixel = c2.pixel = a2.pixel = pixel;
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;
rc.red = rc.green = rc.blue = alpha;
RGetClosestXColor(context, &rc, &a2);
}
if (dest_alpha)
{
XColor da;
/* Alpha gets mixed the same as all the
other color components */
da.pixel = XGetPixel(dest_alpha->image, col, row);
GS_QUERY_COLOR(da);
rc.red = alpha + (da.red >> 8) * (256 - alpha)/256;
rc.green = rc.blue = rc.red;
RGetClosestXColor(context, &rc, &da);
XPutPixel(dest_alpha->image, col, row, da.pixel);
}
if (alpha == 0)
continue; // background unchanged.
if (alpha == 255)
{
rc.red = r;
rc.green = g;
rc.blue = b;
}
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 != c2.pixel)
{
c2.pixel = c0.pixel;
GS_QUERY_COLOR(c0);
}
// mix in alpha to produce RGB out
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);
}
}
else
{
/* Not using alpha, but we still have to set it
in the pixmap */
if (a2.pixel == pixel)
{
rc.red = rc.green = rc.blue = 255;
RGetClosestXColor(context, &rc, &a2);
}
if (dest_alpha)
XPutPixel(dest_alpha->image, col, row, a2.pixel);
rc.red = r;
rc.green = g;
rc.blue = b;
}
/*
* Convert the X RGB value to a colormap entry if we
* don't already have one. Then set the pixel.
* NB. We will not necessarily get a
* color with exactly the rgb components we gave it, so
* we must record those components beforehand.
*/
if (c1.pixel == pixel
|| rc.red != rc2.red
|| rc.green != rc2.green
|| rc.blue != rc2.blue)
{
rc2 = rc;
RGetClosestXColor(context, &rc, &c1);
}
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;
}
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
|| drawMechanism == XGDM_FAST8)
{
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;
case XGDM_FAST8:
InitRGBShiftsAndMasks(5,3,2,3,0,2,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;
unsigned long pixels[CSIZE];
XColor colors[CSIZE];
BOOL empty[CSIZE];
int cind;
for(cind = 0; cind < CSIZE; cind++)
{
empty[cind] = YES;
}
/*
* 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);
GS_QUERY_COLOR(pcolor);
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);
GS_QUERY_COLOR(acolor);
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;
}