netscape-revival
sun-java/awt/win/awt_imagescale.h
/*
* @(#)awt_imagescale.h 1.5 95/11/30 Jim Graham
*
* Copyright (c) 1994 Sun Microsystems, Inc. All Rights Reserved.
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for NON-COMMERCIAL purposes and without
* fee is hereby granted provided that this copyright notice
* appears in all copies. Please refer to the file "copyright.html"
* for further important copyright and licensing information.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
* THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
*/
/*
* It is not optimal to support all image conversion operations with a
* single function. Typically, a number of variations of the generalized
* image function are created based on making a number of assumptions about
* various input and output options which are likely to occur frequently
* and which simplify the conversion loops.
*
* Input data can be:
* 8 or 32 bits per pixel
* all opaque, opaque & transparent, fully alpha qualified
* in its own kind of ColorModel, in one of the default ColorModel
* classes, or in the same ColorModel as the screen
* an opaque bg color or not (to resolve alpha/transparency)
*
* The output (screen data) may need:
* 1, 4, 8, 16, or 32 bits per pixel
* a transparency mask or not
* dithering (< 8 bit output) or closest color approximations
* a different size than the input data
*
* Providing a variation for 8 or 32 bit input is a necessity.
* Providing a variation for output bits per pixel is a necessity. Also,
* different depths will be needed on different machines/displays.
* Providing a variation for dithering versus closest color is also pretty
* much a necessity (if we assume that we will support dithering).
* Providing a variation for a generic ColorModel versus IndexColorModel
* or DirectColorModel is a high priority optimization.
* Providing a variation for simple Alpha is a fairly high priority
* optimization, but can be solved with special case tests inside
* the rendering loop.
*
* The generic function will be available for any of the cases that a
* given platform or implementor does not wish to specialize. This
* function will:
*
* - Choose a source coordinate for the destination coordinate
* - Use a switch and pointer casting for input bits per pixel to
* fetch a pixel into a 32-bit int
* - Use a Java callback to the ColorModel to convert the pixel
* into a 32-bit Alpha/R/G/B quantity
* - If a solid background color is provided, then use the Alpha
* to blend the R/G/B of the pixel with the bg color
* - Else, use a threshold on the Alpha to determine transparency
* and update the transparency mask
* - Use closest color approximation to choose the output pixel as
* a 32-bit int
* - Use a switch and pointer casting for output bits per pixel to
* store the 32-bit pixel into the output buffer
*
* Some support needs to be provided by the device:
*
* - A macro to choose the closest pixel for an RGB triplet
* - A macro to map a pixel quickly to a color cube
* - A macro to allocate an output pixel store for a given
* width/height/depth
* - A macro to allocate a transparency mask
* - Macros to manipulate output pixel stores and masks:
* - Get a pointer to a scan line
* - Store a pixel and increment pointer
* - Increment to next scan line
*
* Note that many of these macros will take as an argument a pointer to
* a structure provided by the device to describe its pixel data and that
* structure can contain pointers to functions to implement any of the
* above.
*/
#include "awt_defs.h"
#include "awt_object.h"
#include "awt_image.h"
extern "C" {
#include "java_awt_image_ColorModel.h"
#include "sun_awt_image_ImageRepresentation.h"
};
/*
* This union is a utility structure for manipulating pixel pointers
* of variable depths.
*/
typedef union {
void FAR* vp;
unsigned char FAR *bp;
unsigned short FAR *sp;
unsigned int FAR *ip;
} pixptr;
extern ColorInfo g_colorInfo[];
#define SCALEFAILURE -1
#define SCALENOOP 0
#define SCALESUCCESS 1
#define ALPHASHIFT 24L
#define REDSHIFT 16L
#define GREENSHIFT 8L
#define BLUESHIFT 0L
#define COLORMASK ((0xff << REDSHIFT) | \
(0xff << GREENSHIFT) | \
(0xff << BLUESHIFT))
#define ALPHABLEND(fg, a, bg) \
((bg) + (((a) * ((fg) - (bg))) / 255L))
/*
* The following mapping is used between coordinates when scaling an
* image:
*
* srcXY = floor(((dstXY + .5) * srcWH) / dstWH)
* = floor((dstXY * srcWH + .5 * srcWH) / dstWH)
* = floor((2 * dstXY * srcWH + srcWH) / (2 * dstWH))
*
* Since the numerator can always be assumed to be non-negative for
* all values of dstXY >= 0 and srcWH,dstWH >= 1, then the floor
* function can be calculated using the standard C integer division
* operator.
*
* To calculate back from a source range of pixels to the destination
* range of pixels that they will affect, we need to find a srcXY
* that satisfies the following inequality based upon the above mapping
* function:
*
* srcXY <= (2 * dstXY * srcWH + srcWH) / (2 * dstWH) < (srcXY+1)
* 2 * srcXY * dstWH <= 2 * dstXY * srcWH + srcWH < 2 * (srcXY+1) * dstWH
*
* To calculate the lowest dstXY that satisfies these constraints, we use
* the first half of the inequality:
*
* 2 * dstXY * srcWH + srcWH >= 2 * srcXY * dstWH
* 2 * dstXY * srcWH >= 2 * srcXY * dstWH - srcWH
* dstXY >= (2 * srcXY * dstWH - srcWH) / (2 * srcWH)
* dstXY = ceil((2 * srcXY * dstWH - srcWH) / (2 * srcWH))
* dstXY = floor((2 * srcXY * dstWH - srcWH + 2*srcWH - 1) / (2 * srcWH))
* dstXY = floor((2 * srcXY * dstWH + srcWH - 1) / (2 * srcWH))
*
* Since the numerator can be shown to be non-negative, we can calculate
* this with the standard C integer division operator.
*
* To calculate the highest dstXY that satisfies these constraints, we use
* the second half of the inequality:
*
* 2 * dstXY * srcWH + srcWH < 2 * (srcXY+1) * dstWH
* 2 * dstXY * srcWH < 2 * (srcXY+1) * dstWH - srcWH
* dstXY < (2 * (srcXY+1) * dstWH - srcWH) / (2 * srcWH)
* dstXY = ceil((2 * (srcXY+1) * dstWH - srcWH) / (2 * srcWH)) - 1
* dstXY = floor((2 * (srcXY+1) * dstWH - srcWH + 2 * srcWH - 1)
* / (2 * srcWH)) - 1
* dstXY = floor((2 * (srcXY+1) * dstWH + srcWH - 1) / (2 * srcWH)) - 1
*
* Again, the numerator is always non-negative so we can use integer division.
*/
#define SRC_XY(dstXY, srcWH, dstWH) \
(((2 * (dstXY) * (srcWH)) + (srcWH)) / (2 * (dstWH)))
#define DEST_XY_RANGE_START(srcXY, srcWH, dstWH) \
(((2 * (srcXY) * (dstWH)) + (srcWH) - 1) / (2 * (srcWH)))
#define DEST_XY_RANGE_END(srcXY, srcWH, dstWH) \
(((2 * ((srcXY) + 1) * (dstWH)) + (srcWH) - 1) / (2 * (srcWH)) - 1)
/*
* The data structures for tracking various pieces of information about
* scaling and converting the image data. REMIND: This should probably
* be moved to a Solaris-specific header file.
*/
typedef struct _DitherError {
int r, g, b;
} DitherError;
typedef struct _AlphaError {
int a;
} AlphaError;
/*
* Macros for manipulating the destination pixel buffer.
*/
#define DeclareDstBufVars(ird) \
pixptr dstP; \
int32_t dstAdjust
#define InitDstBuf(ird, x1, y1, x2, y2) \
(ird->AllocConsumerBuffer(colormodel), \
(dstAdjust = ird->GetBufScan() - (x2 - x1) * ird->GetDepth(colormodel) / 8L) >= 0)
/*
#define SetDstBufLoc(ird, x, y) \
(dstP.vp = ird->GetDstBuf(), \
(dstP.bp += (((ird->GetHeight() - (y+1))*ird->GetBufScan()) + (x) * ird->GetDepth(colormodel) / 8)))
//(dstP.bp += ((y) * ird->GetBufScan() + (x) * ird->GetDepth(colormodel) / 8)))
*/
#define StorePixel(ird, pixel, r, g, b) \
((ird->GetDepth(colormodel) == 24) \
? (*dstP.bp++ = b, \
*dstP.bp++ = g, \
*dstP.bp++ = r) \
: (*dstP.bp++ = pixel))
#define EndDstBufLine(ird) (dstP.bp += dstAdjust)
#define DstBufComplete(ird) \
((ep && dstX1 && (ep = errors) && (ep[0].r = er, \
ep[0].g = eg, \
ep[0].b = eb)), \
(aep && dstX1 && (aep = aerrors) && (aep[0].a = ea)), \
ird->BufDone(dstX1, dstY1, dstX2, dstY2))
/*
* Macros for manipulating a mask.
*/
#define DeclareMaskVars(ird) \
MaskBits *mask = ird->GetMaskBuf(FALSE), *maskp, maskbits, maskcurbit; \
int32_t maskadjust
#define InitMask(ird, x, y) \
(mask || (mask = ird->GetMaskBuf(TRUE), SetMaskLoc(ird, x, y)))
#define SetMaskLoc(ird, x, y) \
((mask || (mask = ird->GetMaskBuf(FALSE))) \
&& (maskp = (mask + ((ird->GetHeight() - (y+1))) * (((ird->GetWidth()+31)&(~31L)) >> 3L) \
+ ((x) >> 3L)), \
maskbits = *maskp, \
maskcurbit = (0x80L >> ((x) & 7L)), \
maskadjust = ((((ird->GetWidth()+31)&(~31L)) >> 3L) \
- ((dstX2 >> 3L) - (dstX1 >> 3L)))))
#define StartMaskLine() \
(mask && (maskbits = *maskp, maskcurbit = (0x80L >> ((dstX1) & 7L))))
#define IncrementMaskBit() \
(((maskcurbit >>= 1) == 0) \
&& (*maskp++ = maskbits, \
maskbits = *maskp, \
maskcurbit = 0x80))
#define ClearMaskBit(ird, x, y) \
(InitMask(ird, x, y), (mask && (maskbits &= ~maskcurbit, \
IncrementMaskBit())))
#define SetMaskBit(ird, x, y) \
(mask && (maskbits |= maskcurbit, IncrementMaskBit()))
#define EndMaskLine() \
(mask && (*maskp = maskbits, maskp += maskadjust))
/*
* Macros for manipulating pixel values.
*/
#define DeclarePixelVars(ird) \
Classjava_awt_image_IndexColorModel *cm; \
Classjava_awt_image_DirectColorModel *dcm; \
unsigned char *cmred, *cmgreen, *cmblue, *cmalpha; \
DitherError *ep, *errors; \
AlphaError *aep, *aerrors; \
RGBQUAD *cp; \
int32_t *recode = 0; \
unsigned char *isrecoded = 0; \
int er, eg, eb, ea, e1, e2, e3; \
int32_t trans_pixel
#define PixelDecodeSetup(ird, colormodel) \
((obj_classblock(colormodel) \
== FindClass(ee, "java/awt/image/IndexColorModel", TRUE)) \
? (cm = (Classjava_awt_image_IndexColorModel *)unhand(colormodel), \
cmgreen = (unsigned char *) unhand(cm->green), \
cmblue = (unsigned char *) unhand(cm->blue), \
cmalpha = (cm->alpha \
? (unsigned char *) unhand(cm->alpha) \
: 0), \
trans_pixel = cm->transparent_index, \
cmred = (unsigned char *) unhand(cm->red)) \
: ((((obj_classblock(colormodel) \
== FindClass(ee, "java/awt/image/DirectColorModel", TRUE)) \
&& (dcm = \
(Classjava_awt_image_DirectColorModel *)unhand(colormodel))\
&& (dcm->red_bits == 8) \
&& (dcm->green_bits == 8) \
&& (dcm->blue_bits == 8) \
&& (dcm->alpha_bits == 8 || dcm->alpha_bits == 0)) \
|| (dcm = 0)), \
cmred = 0))
#define DitherSetup(ird, x1, y1, x2, y2) \
(errors = ep = ird->GetDitherBuf(x1, y1, x2, y2))
#define AlphaErrorInit(ird, x, x1, y1, x2, y2, create) \
((aerrors = aep = ird->AlphaSetup(x1, y1, x2, y2, create)) \
&& (ea = 0, aep += (x - dstX1)))
#define PixelEncodeSetup(ird) \
((srcBPP == 8 && !ep) \
&& (recode = ird->GetRecodeBuf(TRUE), \
isrecoded = ird->GetIsRecodedBuf()))
#define StartDitherLine(ird) \
do { \
if (ep) { \
ep = errors; \
if (dstX1) { \
er = ep[0].r; \
eg = ep[0].g; \
eb = ep[0].b; \
ep += dstX1; \
} else { \
er = eg = eb = 0; \
} \
} \
if (aep) { \
aep = aerrors; \
if (dstX1) { \
ea = aep[0].a; \
aep += dstX1; \
} else { \
ea = 0; \
} \
} \
} while(0)
#define PixelDecode(ird, pixel) \
(cmred \
? (((unsigned int) pixel) > 255) \
? (SignalError(0, JAVAPKG "ArrayIndexOutOfBoundsException", 0), 1)\
: (alpha = ((pixel == trans_pixel) \
? 0 \
: (cmalpha \
? cmalpha[pixel] \
: 255)), \
red = cmred[pixel], \
green = cmgreen[pixel], \
blue = cmblue[pixel], 0) \
: (dcm \
? (alpha = ((dcm->alpha_bits == 0) \
? 255 \
: ((pixel >> dcm->alpha_offset) & 0xff)), \
red = ((pixel >> dcm->red_offset) & 0xff), \
green = ((pixel >> dcm->green_offset) & 0xff), \
blue = ((pixel >> dcm->blue_offset) & 0xff), 0) \
: (rgb = do_execute_java_method(ee, (void *) colormodel,\
"getRGB","(I)I", mb, \
FALSE, pixel), \
alpha = (rgb >> ALPHASHIFT) & 0xff, \
red = (rgb >> REDSHIFT) & 0xff, \
green = (rgb >> GREENSHIFT) & 0xff, \
blue = (rgb >> BLUESHIFT) & 0xff, \
exceptionOccurred(ee))))
#define DitherBound(c) \
(((c) < 0) ? 0 : (((c) > 255) ? 255 : (c)))
#define DitherDist(ep, e1, e2, e3, ec, c) \
do { \
e3 = (ec << 1); \
e1 = e3 + ec; \
e2 = e3 + e1; \
e3 += e2; \
\
ep[0].c += e1 >>= 4; \
ep[1].c += e2 >>= 4; \
ep[2].c += e3 >>= 4; \
ec -= e1 + e2 + e3; \
} while (0)
#define ApplyAlpha(ird) \
if (aep) \
{ \
alpha += aep[1].a; \
aep[1].a = ea; \
if (alpha < 128) \
{ \
ClearMaskBit(ird, dx, dy); \
ea = alpha; \
alpha = 0; \
} \
else \
{ \
SetMaskBit(ird, dx, dy); \
ea = alpha - 255; \
} \
DitherDist(aep, e1, e2, e3, ea, a); \
aep++; \
} \
else if (alpha == 255) \
{ \
SetMaskBit(ird, dx, dy); \
} \
else \
{ \
if (bgalpha != 0) \
{ \
/* Blend colors, ignore bgalpha - it's just a flag */ \
red = ALPHABLEND(red, alpha, bgred); \
green = ALPHABLEND(green, alpha, bggreen); \
blue = ALPHABLEND(blue, alpha, bgblue); \
alpha = 255; \
/* There should never be a mask in this case... */ \
} else { \
if (alpha == 0) { \
ClearMaskBit(ird, dx, dy); \
} else { \
AlphaErrorInit(ird, dx, dstX1, dstY1, dstX2, dstY2, 1); \
if (alpha < 128) { \
ClearMaskBit(ird, dx, dy); \
ea = alpha; \
alpha = 0; \
} else { \
SetMaskBit(ird, dx, dy); \
ea = alpha - 255; \
} \
if (aep) { \
DitherDist(aep, e1, e2, e3, ea, a); \
aep++; \
} \
} \
} \
}
#define DitherPixel(ird, pixel, red, green, blue) \
if (ep) { \
/* add previous errors */ \
red += ep[1].r; \
green += ep[1].g; \
blue += ep[1].b; \
\
/* bounds checking */ \
e1 = DitherBound(red); \
e2 = DitherBound(green); \
e3 = DitherBound(blue); \
\
/* Store the closest color in the destination pixel */ \
pixel = ird->DitherMap(e1, e2, e3); \
cp = ird->PixelColor(pixel); \
\
/* Set the error from the previous lap */ \
ep[1].r = er; ep[1].g = eg; ep[1].b = eb; \
\
/* compute the errors */ \
er = e1 - cp->rgbRed; eg = e2 - cp->rgbGreen; eb = e3 - cp->rgbBlue; \
\
/* distribute the errors */ \
DitherDist(ep, e1, e2, e3, er, r); \
DitherDist(ep, e1, e2, e3, eg, g); \
DitherDist(ep, e1, e2, e3, eb, b); \
ep++; \
}
#define PixelEncode(ird, pixel, r, g, b) \
((alpha == 0) \
? (pixel = ird->DitherMap(0, 0, 0)) \
: (recode \
? (isrecoded[pixel] \
? pixel = recode[pixel] \
: (isrecoded[pixel]++, \
pixel = (recode[pixel] \
= ird->ColorMatch(r, g, b)))) \
: ((ep == 0) ? pixel = ird->ColorMatch(r, g, b) : pixel)))
extern int GenericImageConvert(struct Hjava_awt_image_ColorModel *colormodel,
int32_t bgcolor, int32_t srcX, int32_t srcY, int32_t srcW, int32_t srcH,
void *srcpix, int32_t srcOff, int32_t srcBPP, int32_t srcScan,
int32_t srcTotalWidth, int32_t srcTotalHeight,
int32_t dstTotalWidth, int32_t dstTotalHeight,
AwtImage *ird);