// xImaPal.cpp : Palette and Pixel functions /* 07/08/2001 v1.00 - Davide Pizzolato - www.xdp.it * CxImage version 7.0.2 07/Feb/2011 */ #include "ximage.h" #ifndef min #define min(a,b) (((a)<(b))?(a):(b)) #endif #ifndef max #define max(a,b) (((a)>(b))?(a):(b)) #endif //////////////////////////////////////////////////////////////////////////////// /** * returns the palette dimension in byte */ uint32_t CxImage::GetPaletteSize() { return (head.biClrUsed * sizeof(RGBQUAD)); } //////////////////////////////////////////////////////////////////////////////// void CxImage::SetPaletteColor(uint8_t idx, uint8_t r, uint8_t g, uint8_t b, uint8_t alpha) { if ((pDib)&&(head.biClrUsed)){ uint8_t* iDst = (uint8_t*)(pDib) + sizeof(BITMAPINFOHEADER); if (idx=head.biWidth)||(y>=head.biHeight)) { if (info.nBkgndIndex >= 0) return (uint8_t)info.nBkgndIndex; else return *info.pImage; } if (head.biBitCount==8){ return info.pImage[y*info.dwEffWidth + x]; } else { uint8_t pos; uint8_t iDst= info.pImage[y*info.dwEffWidth + (x*head.biBitCount >> 3)]; if (head.biBitCount==4){ pos = (uint8_t)(4*(1-x%2)); iDst &= (0x0F<> pos); } else if (head.biBitCount==1){ pos = (uint8_t)(7-x%8); iDst &= (0x01<> pos); } } return 0; } //////////////////////////////////////////////////////////////////////////////// uint8_t CxImage::BlindGetPixelIndex(const int32_t x,const int32_t y) { #ifdef _DEBUG if ((pDib==NULL) || (head.biClrUsed==0) || !IsInside(x,y)) #if CXIMAGE_SUPPORT_EXCEPTION_HANDLING throw 0; #else return 0; #endif #endif if (head.biBitCount==8){ return info.pImage[y*info.dwEffWidth + x]; } else { uint8_t pos; uint8_t iDst= info.pImage[y*info.dwEffWidth + (x*head.biBitCount >> 3)]; if (head.biBitCount==4){ pos = (uint8_t)(4*(1-x%2)); iDst &= (0x0F<> pos); } else if (head.biBitCount==1){ pos = (uint8_t)(7-x%8); iDst &= (0x01<> pos); } } return 0; } //////////////////////////////////////////////////////////////////////////////// RGBQUAD CxImage::GetPixelColor(int32_t x,int32_t y, bool bGetAlpha) { // RGBQUAD rgb={0,0,0,0}; RGBQUAD rgb=info.nBkgndColor; // if ((pDib==NULL)||(x<0)||(y<0)|| (x>=head.biWidth)||(y>=head.biHeight)){ if (info.nBkgndIndex >= 0){ if (head.biBitCount<24) return GetPaletteColor((uint8_t)info.nBkgndIndex); else return info.nBkgndColor; } else if (pDib) return GetPixelColor(0,0); return rgb; } if (head.biClrUsed){ rgb = GetPaletteColor(BlindGetPixelIndex(x,y)); } else { uint8_t* iDst = info.pImage + y*info.dwEffWidth + x*3; rgb.rgbBlue = *iDst++; rgb.rgbGreen= *iDst++; rgb.rgbRed = *iDst; } #if CXIMAGE_SUPPORT_ALPHA if (pAlpha && bGetAlpha) rgb.rgbReserved = BlindAlphaGet(x,y); #else rgb.rgbReserved = 0; #endif //CXIMAGE_SUPPORT_ALPHA return rgb; } //////////////////////////////////////////////////////////////////////////////// /** * This is (a bit) faster version of GetPixelColor. * It tests bounds only in debug mode (_DEBUG defined). * * It is an error to request out-of-borders pixel with this method. * In DEBUG mode an exception will be thrown, and data will be violated in non-DEBUG mode. * \author ***bd*** 2.2004 */ RGBQUAD CxImage::BlindGetPixelColor(const int32_t x,const int32_t y, bool bGetAlpha) { RGBQUAD rgb; #ifdef _DEBUG if ((pDib==NULL) || !IsInside(x,y)) #if CXIMAGE_SUPPORT_EXCEPTION_HANDLING throw 0; #else {rgb.rgbReserved = 0; return rgb;} #endif #endif if (head.biClrUsed){ rgb = GetPaletteColor(BlindGetPixelIndex(x,y)); } else { uint8_t* iDst = info.pImage + y*info.dwEffWidth + x*3; rgb.rgbBlue = *iDst++; rgb.rgbGreen= *iDst++; rgb.rgbRed = *iDst; rgb.rgbReserved = 0; //needed for images without alpha layer } #if CXIMAGE_SUPPORT_ALPHA if (pAlpha && bGetAlpha) rgb.rgbReserved = BlindAlphaGet(x,y); #else rgb.rgbReserved = 0; #endif //CXIMAGE_SUPPORT_ALPHA return rgb; } //////////////////////////////////////////////////////////////////////////////// uint8_t CxImage::GetPixelGray(int32_t x, int32_t y) { RGBQUAD color = GetPixelColor(x,y); return (uint8_t)RGB2GRAY(color.rgbRed,color.rgbGreen,color.rgbBlue); } //////////////////////////////////////////////////////////////////////////////// void CxImage::BlindSetPixelIndex(int32_t x,int32_t y,uint8_t i) { #ifdef _DEBUG if ((pDib==NULL)||(head.biClrUsed==0)|| (x<0)||(y<0)||(x>=head.biWidth)||(y>=head.biHeight)) #if CXIMAGE_SUPPORT_EXCEPTION_HANDLING throw 0; #else return; #endif #endif if (head.biBitCount==8){ info.pImage[y*info.dwEffWidth + x]=i; return; } else { uint8_t pos; uint8_t* iDst= info.pImage + y*info.dwEffWidth + (x*head.biBitCount >> 3); if (head.biBitCount==4){ pos = (uint8_t)(4*(1-x%2)); *iDst &= ~(0x0F<=head.biWidth)||(y>=head.biHeight)) return ; if (head.biBitCount==8){ info.pImage[y*info.dwEffWidth + x]=i; return; } else { uint8_t pos; uint8_t* iDst= info.pImage + y*info.dwEffWidth + (x*head.biBitCount >> 3); if (head.biBitCount==4){ pos = (uint8_t)(4*(1-x%2)); *iDst &= ~(0x0F<=head.biWidth)||(y>=head.biHeight)) #if CXIMAGE_SUPPORT_EXCEPTION_HANDLING throw 0; #else return; #endif #endif if (head.biClrUsed) BlindSetPixelIndex(x,y,GetNearestIndex(c)); else { uint8_t* iDst = info.pImage + y*info.dwEffWidth + x*3; *iDst++ = c.rgbBlue; *iDst++ = c.rgbGreen; *iDst = c.rgbRed; } #if CXIMAGE_SUPPORT_ALPHA if (bSetAlpha) AlphaSet(x,y,c.rgbReserved); #endif //CXIMAGE_SUPPORT_ALPHA } //////////////////////////////////////////////////////////////////////////////// void CxImage::SetPixelColor(int32_t x,int32_t y,RGBQUAD c, bool bSetAlpha) { if ((pDib==NULL)||(x<0)||(y<0)|| (x>=head.biWidth)||(y>=head.biHeight)) return; if (head.biClrUsed) BlindSetPixelIndex(x,y,GetNearestIndex(c)); else { uint8_t* iDst = info.pImage + y*info.dwEffWidth + x*3; *iDst++ = c.rgbBlue; *iDst++ = c.rgbGreen; *iDst = c.rgbRed; } #if CXIMAGE_SUPPORT_ALPHA if (bSetAlpha) AlphaSet(x,y,c.rgbReserved); #endif //CXIMAGE_SUPPORT_ALPHA } //////////////////////////////////////////////////////////////////////////////// /** * Blends the current pixel color with a new color. * \param x,y = pixel * \param c = new color * \param blend = can be from 0 (no effect) to 1 (full effect). * \param bSetAlpha = if true, blends also the alpha component stored in c.rgbReserved */ void CxImage::BlendPixelColor(int32_t x,int32_t y,RGBQUAD c, float blend, bool bSetAlpha) { if ((pDib==NULL)||(x<0)||(y<0)|| (x>=head.biWidth)||(y>=head.biHeight)) return; int32_t a0 = (int32_t)(256*blend); int32_t a1 = 256 - a0; RGBQUAD c0 = BlindGetPixelColor(x,y); c.rgbRed = (uint8_t)((c.rgbRed * a0 + c0.rgbRed * a1)>>8); c.rgbBlue = (uint8_t)((c.rgbBlue * a0 + c0.rgbBlue * a1)>>8); c.rgbGreen = (uint8_t)((c.rgbGreen * a0 + c0.rgbGreen * a1)>>8); if (head.biClrUsed) BlindSetPixelIndex(x,y,GetNearestIndex(c)); else { uint8_t* iDst = info.pImage + y*info.dwEffWidth + x*3; *iDst++ = c.rgbBlue; *iDst++ = c.rgbGreen; *iDst = c.rgbRed; #if CXIMAGE_SUPPORT_ALPHA if (bSetAlpha) AlphaSet(x,y,c.rgbReserved); #endif //CXIMAGE_SUPPORT_ALPHA } } //////////////////////////////////////////////////////////////////////////////// /** * Returns the best palette index that matches a specified color. */ uint8_t CxImage::GetNearestIndex(RGBQUAD c) { if ((pDib==NULL)||(head.biClrUsed==0)) return 0; // check matching with the previous result if (info.last_c_isvalid && (*(int32_t*)&info.last_c == *(int32_t*)&c)) return info.last_c_index; info.last_c = c; info.last_c_isvalid = true; uint8_t* iDst = (uint8_t*)(pDib) + sizeof(BITMAPINFOHEADER); int32_t distance=200000; int32_t i,j = 0; int32_t k,l; int32_t m = (int32_t)(head.biClrImportant==0 ? head.biClrUsed : head.biClrImportant); for(i=0,l=0;i100) perc=100; for(i=0;i=0){ if (head.biClrUsed){ if (GetPixelIndex(x,y) == info.nBkgndIndex) return true; } else { RGBQUAD ct = info.nBkgndColor; RGBQUAD c = GetPixelColor(x,y,false); if (*(int32_t*)&c==*(int32_t*)&ct) return true; } } #if CXIMAGE_SUPPORT_ALPHA if (pAlpha) return AlphaGet(x,y)==0; #endif return false; } //////////////////////////////////////////////////////////////////////////////// bool CxImage::GetTransparentMask(CxImage* iDst) { if (!pDib) return false; CxImage tmp; tmp.Create(head.biWidth, head.biHeight, 1, GetType()); tmp.SetStdPalette(); tmp.Clear(0); for(int32_t y=0; yTransfer(tmp); else Transfer(tmp); return true; } //////////////////////////////////////////////////////////////////////////////// /** * Checks if image has the same palette, if any. * \param img = image to compare. * \param bCheckAlpha = check also the rgbReserved field. */ bool CxImage::IsSamePalette(CxImage &img, bool bCheckAlpha) { if (head.biClrUsed != img.head.biClrUsed) return false; if (head.biClrUsed == 0) return false; RGBQUAD c1,c2; for (uint32_t n=0; n256) { head.biClrImportant = 0; return; } switch(head.biBitCount){ case 1: head.biClrImportant = min(ncolors,2); break; case 4: head.biClrImportant = min(ncolors,16); break; case 8: head.biClrImportant = ncolors; break; } return; } //////////////////////////////////////////////////////////////////////////////// /** * Returns pointer to pixel. Currently implemented only for truecolor images. * * \param x,y - coordinates * * \return pointer to first byte of pixel data * * \author ***bd*** 2.2004 */ void* CxImage::BlindGetPixelPointer(const int32_t x, const int32_t y) { #ifdef _DEBUG if ((pDib==NULL) || !IsInside(x,y)) #if CXIMAGE_SUPPORT_EXCEPTION_HANDLING throw 0; #else return 0; #endif #endif if (!IsIndexed()) return info.pImage + y*info.dwEffWidth + x*3; else return 0; } //////////////////////////////////////////////////////////////////////////////// void CxImage::DrawLine(int32_t StartX, int32_t EndX, int32_t StartY, int32_t EndY, COLORREF cr) { DrawLine(StartX, EndX, StartY, EndY, RGBtoRGBQUAD(cr)); } //////////////////////////////////////////////////////////////////////////////// void CxImage::DrawLine(int32_t StartX, int32_t EndX, int32_t StartY, int32_t EndY, RGBQUAD color, bool bSetAlpha) { if (!pDib) return; ////////////////////////////////////////////////////// // Draws a line using the Bresenham line algorithm // Thanks to Jordan DeLozier ////////////////////////////////////////////////////// int32_t x1 = StartX; int32_t y1 = StartY; int32_t x = x1; // Start x off at the first pixel int32_t y = y1; // Start y off at the first pixel int32_t x2 = EndX; int32_t y2 = EndY; int32_t xinc1,xinc2,yinc1,yinc2; // Increasing values int32_t den, num, numadd,numpixels; int32_t deltax = abs(x2 - x1); // The difference between the x's int32_t deltay = abs(y2 - y1); // The difference between the y's // Get Increasing Values if (x2 >= x1) { // The x-values are increasing xinc1 = 1; xinc2 = 1; } else { // The x-values are decreasing xinc1 = -1; xinc2 = -1; } if (y2 >= y1) { // The y-values are increasing yinc1 = 1; yinc2 = 1; } else { // The y-values are decreasing yinc1 = -1; yinc2 = -1; } // Actually draw the line if (deltax >= deltay) // There is at least one x-value for every y-value { xinc1 = 0; // Don't change the x when numerator >= denominator yinc2 = 0; // Don't change the y for every iteration den = deltax; num = deltax / 2; numadd = deltay; numpixels = deltax; // There are more x-values than y-values } else // There is at least one y-value for every x-value { xinc2 = 0; // Don't change the x for every iteration yinc1 = 0; // Don't change the y when numerator >= denominator den = deltay; num = deltay / 2; numadd = deltax; numpixels = deltay; // There are more y-values than x-values } for (int32_t curpixel = 0; curpixel <= numpixels; curpixel++) { // Draw the current pixel SetPixelColor(x,y,color,bSetAlpha); num += numadd; // Increase the numerator by the top of the fraction if (num >= den) // Check if numerator >= denominator { num -= den; // Calculate the new numerator value x += xinc1; // Change the x as appropriate y += yinc1; // Change the y as appropriate } x += xinc2; // Change the x as appropriate y += yinc2; // Change the y as appropriate } } //////////////////////////////////////////////////////////////////////////////// bool CxImage::SetRectColor(RECT& rect, RGBQUAD color, bool bSetAlpha) { return SetRectColor(rect.left, rect.top, rect.right, rect.bottom, color, bSetAlpha); } //////////////////////////////////////////////////////////////////////////////// bool CxImage::SetRectColor(int32_t left, int32_t top, int32_t right, int32_t bottom, RGBQUAD color, bool bSetAlpha) { if (!pDib) return false; int32_t startx = max(0L,min(left,head.biWidth)); int32_t endx = max(0L,min(right,head.biWidth)); int32_t starty = head.biHeight - max(0L,min(top,head.biHeight)); int32_t endy = head.biHeight - max(0L,min(bottom,head.biHeight)); if (startx==endx || starty==endy) return true; if (startx>endx) {int32_t tmp=startx; startx=endx; endx=tmp;} if (starty>endy) {int32_t tmp=starty; starty=endy; endy=tmp;} switch (head.biBitCount) { case 1: case 4: { uint8_t n = GetNearestIndex(color); for(int32_t y=starty; y> 3; uint8_t* pDest = info.pImage + starty * info.dwEffWidth + (startx*head.biBitCount >> 3); for(int32_t y=starty; y> 3; uint8_t* pSrc = (uint8_t*)malloc(linelen); if (0 == pSrc) return false; for(int32_t x=0; x> 3); for(int32_t y=starty; y