/******************************************************************************* * Copyright (c) 2000, 2005 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ #include "NgImageData.h" static UBYTE4 RoundRow (UBYTE4 width) { UBYTE4 result = (width + RowRounding - 1) & ~(RowRounding - 1) ; return result ; } void NgBitmapImageInit (ng_bitmap_image_t *image) { NgBitmapImageClearData (image); } void NgBitmapImageFree (ng_bitmap_image_t *image) { NgFree (image->color_map); NgFree (image->image_data); NgFree (image->alpha_data); } void NgBitmapImageClearData (ng_bitmap_image_t *image) { image->bit_count = 0; image->image_width = 0; image->image_height = 0; image->color_count = 0; image->color_map = NULL; image->image_data = NULL; image->alpha_data = NULL; image->transparent_pixel = -1; } void NgBitmapImageSetSize(ng_bitmap_image_t *image, UBYTE4 color_count, UBYTE4 bits, UBYTE4 width, UBYTE4 height) { NgFree (image->color_map); NgFree (image->image_data); NgBitmapImageClearData (image); switch (bits) { case 1: case 2: case 4: case 8: { UBYTE4 bitsize; UBYTE4 bytecount; image->bit_count = bits; image->color_count = color_count; image->image_width = width; image->image_height = height; image->color_map = (ng_color_map_entry_t *) NgMalloc (sizeof(ng_color_map_entry_t) * image->color_count); NgMemSet (image->color_map, 0, sizeof (ng_color_map_entry_t) * image->color_count); bitsize = image->bit_count * image->image_width; image->row_width = RoundRow ((bitsize + 7)/8); bytecount = image->row_width * image->image_height; image->image_data = (UBYTE1 *) NgMalloc (bytecount); NgMemSet (image->image_data, 0, (BYTE4)bytecount); } break ; case 16: { image->bit_count = bits; image->color_count = color_count; image->image_width = width; image->image_height = height; image->row_width = RoundRow (2 * image->image_width); image->image_data = (UBYTE1 *) NgMalloc (image->row_width * image->image_height); NgMemSet (image->image_data, 0, image->row_width * image->image_height); } break; case 24: { image->bit_count = bits; image->color_count = color_count; image->image_width = width; image->image_height = height; image->row_width = RoundRow (3 * image->image_width); image->image_data = (UBYTE1 *) NgMalloc (image->row_width * image->image_height); NgMemSet (image->image_data, 0, image->row_width * image->image_height); } break; case 32: { image->bit_count = bits; image->color_count = color_count; image->image_width = width; image->image_height = height; image->row_width = RoundRow (4 * image->image_width); image->image_data = (UBYTE1 *) NgMalloc (image->row_width * image->image_height); NgMemSet (image->image_data, 0, image->row_width * image->image_height); } break ; default: NgError (ERR_INVALID_BIT_COUNT, NULL); } } ng_color_map_entry_t *NgBitmapImageColorMap (ng_bitmap_image_t *image, UBYTE4 index) { if (index >= image->color_count) { NgError (ERR_SUBSCRIPT_OUT_OF_RANGE, "Error NgBitmapImageColorMap failed"); return NULL; } return &image->color_map [index] ; } /* blit constants */ #define TYPE_INDEX_1_MSB 1 #define TYPE_INDEX_1_LSB 2 #define TYPE_INDEX_2 3 #define TYPE_INDEX_4 4 #define TYPE_INDEX_8 5 #define TYPE_GENERIC_24 6 #define TYPE_GENERIC_8 7 #define TYPE_GENERIC_16_MSB 8 #define TYPE_GENERIC_16_LSB 9 #define TYPE_GENERIC_32_MSB 10 #define TYPE_GENERIC_32_LSB 11 /** * Computes the required channel shift from a mask. */ UBYTE4 getChannelShift(UBYTE4 mask) { UBYTE4 i; if (mask == 0) return 0; for (i = 0; ((mask & 1) == 0) && (i < 32); ++i) { mask >>= 1; } return i; } /** * Computes the required channel width (depth) from a mask. */ UBYTE4 getChannelWidth(UBYTE4 mask, UBYTE4 shift) { UBYTE4 i; if (mask == 0) return 0; mask >>= shift; for (i = shift; ((mask & 1) != 0) && (i < 32); ++i) { mask >>= 1; } return i - shift; } /** * Blits a direct palette image into a direct palette image. * * srcData the source byte array containing image data * srcStride the source number of bytes per line * srcWidth the width of the source blit region * srcHeight the height of the source blit region * destData the destination byte array containing image data * destDepth the destination depth: one of 8, 16, 24, 32 * destStride the destination number of bytes per line * destOrder the destination byte ordering: 0 for LSB, 1 otherwise * ignored if destDepth is not 16 or 32 * destRedMask the destination red channel mask * destGreenMask the destination green channel mask * destBlueMask the destination blue channel mask * * It is assumed that. * srcDepth: 24 - BGR ordering (BMP format) * no alpha * srcX: 0 * srcY: 0 * destX: 0 * destY: 0 * destWidth: same as srcWidth * destHeight: same as srcHeight */ void NgBitmapImageBlitDirectToDirect( UBYTE1 *srcData, BYTE4 srcStride, BYTE4 srcWidth, BYTE4 srcHeight, UBYTE1 *destData, BYTE4 destDepth, BYTE4 destStride, BYTE4 destOrder, UBYTE4 destRedMask, UBYTE4 destGreenMask, UBYTE4 destBlueMask) { BYTE4 srcX = 0, srcY = 0, destX = 0, destY = 0, destWidth = srcWidth, destHeight = srcHeight; BYTE4 sbpp, stype, spr, dbpp, dtype, dpr, dprxi, dpryi, dp, sp, dy, dx; BYTE4 destRedShift, destRedWidth; BYTE4 destRedPreShift, destGreenShift, destGreenWidth, destGreenPreShift; BYTE4 destBlueShift, destBlueWidth, destBluePreShift; UBYTE1 r, g, b; UBYTE4 data; /*** Prepare source-related data ***/ sbpp = 3; stype = TYPE_GENERIC_24; spr = srcY * srcStride + srcX * sbpp; /*** Prepare destination-related data ***/ switch (destDepth) { case 8: dbpp = 1; dtype = TYPE_GENERIC_8; break; case 16: dbpp = 2; dtype = (destOrder != 0) ? TYPE_GENERIC_16_MSB : TYPE_GENERIC_16_LSB; break; case 24: dbpp = 3; dtype = TYPE_GENERIC_24; break; case 32: dbpp = 4; dtype = (destOrder != 0) ? TYPE_GENERIC_32_MSB : TYPE_GENERIC_32_LSB; break; default: return; } dpr = destY * destStride + destX * dbpp; dprxi = dbpp; dpryi = destStride; /*** Blit ***/ dp = dpr; sp = spr; /*** Comprehensive blit (apply transformations) ***/ destRedShift = getChannelShift(destRedMask); destRedWidth = getChannelWidth(destRedMask, destRedShift); destRedPreShift = 8 - destRedWidth; destGreenShift = getChannelShift(destGreenMask); destGreenWidth = getChannelWidth(destGreenMask, destGreenShift); destGreenPreShift = 8 - destGreenWidth; destBlueShift = getChannelShift(destBlueMask); destBlueWidth = getChannelWidth(destBlueMask, destBlueShift); destBluePreShift = 8 - destBlueWidth; r = 0; g = 0; b = 0; for (dy = destHeight; dy > 0; --dy, sp = spr += srcStride, dp = dpr += dpryi) { for (dx = destWidth; dx > 0; --dx, dp += dprxi) { /*** READ NEXT PIXEL ASSUMING BGR ordering (BMP format) ***/ b = srcData[sp]; g = srcData[sp + 1]; r = srcData[sp + 2]; sp += 3; /*** WRITE NEXT PIXEL ***/ data = (r >> destRedPreShift << destRedShift) | (g >> destGreenPreShift << destGreenShift) | (b >> destBluePreShift << destBlueShift); switch (dtype) { case TYPE_GENERIC_8: { destData[dp] = (UBYTE1) data; } break; case TYPE_GENERIC_16_MSB: { destData[dp] = (UBYTE1) (data >> 8); destData[dp + 1] = (UBYTE1) (data & 0xff); } break; case TYPE_GENERIC_16_LSB: { destData[dp] = (UBYTE1) (data & 0xff); destData[dp + 1] = (UBYTE1) (data >> 8); } break; case TYPE_GENERIC_24: { destData[dp] = (UBYTE1) (data >> 16); destData[dp + 1] = (UBYTE1) (data >> 8); destData[dp + 2] = (UBYTE1) (data & 0xff); } break; case TYPE_GENERIC_32_MSB: { destData[dp] = (UBYTE1) (data >> 24); destData[dp + 1] = (UBYTE1) (data >> 16); destData[dp + 2] = (UBYTE1) (data >> 8); destData[dp + 3] = (UBYTE1) (data & 0xff); } break; case TYPE_GENERIC_32_LSB: { destData[dp] = (UBYTE1) (data & 0xff); destData[dp + 1] = (UBYTE1) (data >> 8); destData[dp + 2] = (UBYTE1) (data >> 16); destData[dp + 3] = (UBYTE1) (data >> 24); } break; } } } } /** * Create a simple hash table used when converting direct colors to values in a palette * Each bucket stores the RGB codes and the corresponding palette index. * The key is made from the RGB values. * It is used as a cache. New entries colliding with older ones simply * replace them. */ ng_palette_bucket_t *NgRGBIndexCreate () { ng_palette_bucket_t *table = (ng_palette_bucket_t *)NgMalloc (RGBIndexTableSize * sizeof (ng_palette_bucket_t)); NgMemSet (table, 0, RGBIndexTableSize * sizeof (ng_palette_bucket_t)); return table; } void NgRGBIndexFree (ng_palette_bucket_t *table) { NgFree (table); } void NgRGBIndexSet (ng_palette_bucket_t *table, UBYTE1 r, UBYTE1 g, UBYTE1 b, UBYTE1 index) { int i = (r * g * b) % RGBIndexTableSize; table[i].blue = b; table[i].green = g; table[i].red = r; table[i].index = index; table[i].isSet = 1; } int NgRGBIndexGet (ng_palette_bucket_t *table, UBYTE1 r, UBYTE1 g, UBYTE1 b) { int i = (r * g * b) % RGBIndexTableSize; if (table[i].isSet && table[i].blue == b && table[i].green == g && table[i].red == r) return table[i].index; return -1; } /** * Blits a direct palette image into an index palette image. * * srcData the source byte array containing image data * srcStride the source number of bytes per line * srcX the top-left x-coord of the source blit region * srcY the top-left y-coord of the source blit region * srcWidth the width of the source blit region * srcHeight the height of the source blit region * destData the destination byte array containing image data * destDepth the destination depth: one of 1, 2, 4, 8 * destStride the destination number of bytes per line * destOrder the destination byte ordering: 0 if LSB, 1 otherwise; * ignored if destDepth is not 1 * destX the top-left x-coord of the destination blit region * destY the top-left y-coord of the destination blit region * destWidth the width of the destination blit region * destHeight the height of the destination blit region * destColors the destination palette red green blue component intensities * destNumColors the number of colors in destColors * * It is assumed that. * srcDepth: 24 - BGR ordering (BMP format) * no alpha * srcX: 0 * srcY: 0 * destX: 0 * destY: 0 * destWidth: same as srcWidth * destHeight: same as srcHeight */ void NgBitmapImageBlitDirectToPalette( UBYTE1 *srcData, BYTE4 srcStride, BYTE4 srcWidth, BYTE4 srcHeight, UBYTE1 *destData, BYTE4 destDepth, BYTE4 destStride, BYTE4 destOrder, UBYTE1 *destColors, int destNumColors) { BYTE4 srcX = 0, srcY = 0, destX = 0, destY = 0, destWidth = srcWidth, destHeight = srcHeight; BYTE4 sbpp, spr, dtype, dpr, dp, sp, destPaletteSize, dy, dx, j, dr, dg, db, distance, minDistance; UBYTE1 r = 0, g = 0, b = 0, index = 0; int storedIndex; ng_palette_bucket_t *RGBIndexTable; /*** Prepare source-related data ***/ sbpp = 3; spr = srcY * srcStride + srcX * sbpp; /*** Prepare destination-related data ***/ switch (destDepth) { case 8: dtype = TYPE_INDEX_8; break; case 4: destStride <<= 1; dtype = TYPE_INDEX_4; break; case 2: destStride <<= 2; dtype = TYPE_INDEX_2; break; case 1: destStride <<= 3; dtype = (destOrder != 0) ? TYPE_INDEX_1_MSB : TYPE_INDEX_1_LSB; break; default: return; } dpr = destY * destStride + destX; dp = dpr; sp = spr; destPaletteSize = destNumColors; RGBIndexTable = NgRGBIndexCreate (); for (dy = destHeight; dy > 0; --dy, sp = spr += srcStride, dp = dpr += destStride) { for (dx = destWidth; dx > 0; --dx, dp += 1) { /*** READ NEXT PIXEL ASSUMING BGR ordering (BMP format) ***/ b = srcData[sp]; g = srcData[sp+1]; r = srcData[sp+2]; sp += 3; /*** MAP COLOR TO THE PALETTE ***/ storedIndex = NgRGBIndexGet (RGBIndexTable, r, g, b); if (storedIndex >= 0) { index = (UBYTE1) storedIndex; } else { for (j = 0, minDistance = 0x7fffffff; j < destPaletteSize; ++j) { dr = (destColors[j*3] & 0xff) - r; dg = (destColors[j*3+1] & 0xff) - g; db = (destColors[j*3+2] & 0xff) - b; distance = dr * dr + dg * dg + db * db; if (distance < minDistance) { index = (UBYTE1)j; if (distance == 0) break; minDistance = distance; } } NgRGBIndexSet (RGBIndexTable, r, g, b, index); } /*** WRITE NEXT PIXEL ***/ switch (dtype) { case TYPE_INDEX_8: destData[dp] = (UBYTE1) index; break; case TYPE_INDEX_4: if ((dp & 1) != 0) destData[dp >> 1] = ((destData[dp >> 1] & 0xf0) | index); else destData[dp >> 1] = ((destData[dp >> 1] & 0x0f) | (index << 4)); break; case TYPE_INDEX_2: { int shift = 6 - (dp & 3) * 2; destData[dp >> 2] = ((destData[dp >> 2] & ~(0x03 << shift)) | (index << shift)); } break; case TYPE_INDEX_1_MSB: { int shift = 7 - (dp & 7); destData[dp >> 3] = ((destData[dp >> 3] & ~(0x01 << shift)) | (index << shift)); } break; case TYPE_INDEX_1_LSB: { int shift = dp & 7; destData[dp >> 3] = ((destData[dp >> 3] & ~(0x01 << shift)) | (index << shift)); } break; } } } NgRGBIndexFree (RGBIndexTable); }