static void RandomDitherBitmap(CSBitmap* pBitmap, int ditherAmount) { const int width = GetWidth(pBitmap); const int height = GetHeight(pBitmap); const int numChannels = GetChannels(pBitmap); if (numChannels < 3) return; for (int y = 0; y < height; ++y) { uint8_t* pPixel = GetLinePtr(pBitmap, y); for (int x = 0; x < width; ++x) { int offset = (rand() % ditherAmount) - (ditherAmount / 2); for (int chan = 0; chan < 3; ++chan) { int Value = pPixel[chan] + offset; if (Value < 0) Value = 0; if (Value > 255) Value = 255; pPixel[chan] = Value; } pPixel += numChannels; } } }
uint8_t *GetPixelPtr(CSBitmap* pBitmap, int x, int y) { assert(HasBitmap(pBitmap)); assert(y >= 0 && y < GetHeight(pBitmap)); assert(x >= 0 && x < GetWidth(pBitmap)); return GetLinePtr(pBitmap, y) + x * GetChannels(pBitmap); }
//=========================================================================== //=========================================================================== bool KImage::GaussianBlur(double dblRadius) { bool boolError = false; if (BeginDirectAccessOnLines()) { switch(GetPixelBits()) { case 1: assert(false); boolError = true; break; case 8: { BYTE **pLineInput = new BYTE *[intHeight]; for (int intY = intHeight - 1; intY >= 0; intY--) pLineInput[intY] = (BYTE *)(GetLinePtr(intY)); __GaussianBlurOneChannel(intWidth, intHeight, pLineInput, pLineInput, dblRadius); delete [] pLineInput; break; } case 24: { BYTE **pLineInput = new BYTE *[intHeight]; for (int intY = intHeight - 1; intY >= 0; intY--) pLineInput[intY] = new BYTE[intWidth]; for (int intChannel = 0; intChannel < 3; intChannel++) { for (int intY = intHeight - 1; intY >= 0; intY--) { BYTE *line = (BYTE *)GetLinePtr(intY); for (int intX = 0; intX < intWidth; intX++) pLineInput[intY][intX] = line[3 * intX + intChannel]; } __GaussianBlurOneChannel(intWidth, intHeight, pLineInput, pLineInput, dblRadius); for (int intY = intHeight - 1; intY >= 0; intY--) { BYTE *line = (BYTE *)GetLinePtr(intY); for (int intX = 0; intX < intWidth; intX++) line[3 * intX + intChannel] = pLineInput[intY][intX]; } } for (int intY = intHeight - 1; intY >= 0; intY--) delete [] pLineInput[intY]; delete [] pLineInput; break; } default: assert(false); boolError = true; break; } EndDirectAccessOnLines(); } return !boolError; }
static void OrderedDitherBitmap(CSBitmap* pBitmap, int ditherAmount) { const int DITHERSIZE = 16; // A sixteen by sixteen dither table holds 256 values, // which is enough to give us dither values from zero to 255. int DitherArray[DITHERSIZE][DITHERSIZE] = { { 0, 128, 32, 160, 8, 136, 40, 168, 2, 130, 34, 162, 10, 138, 42, 170, }, {192, 64, 224, 96, 200, 72, 232, 104, 194, 66, 226, 98, 202, 74, 234, 106, }, {48, 176, 16, 144, 56, 184, 24, 152, 50, 178, 18, 146, 58, 186, 26, 154, }, {240, 112, 208, 80, 248, 120, 216, 88, 242, 114, 210, 82, 250, 122, 218, 90, }, {12, 140, 44, 172, 4, 132, 36, 164, 14, 142, 46, 174, 6, 134, 38, 166, }, {204, 76, 236, 108, 196, 68, 228, 100, 206, 78, 238, 110, 198, 70, 230, 102, }, {60, 188, 28, 156, 52, 180, 20, 148, 62, 190, 30, 158, 54, 182, 22, 150, }, {252, 124, 220, 92, 244, 116, 212, 84, 254, 126, 222, 94, 246, 118, 214, 86, }, { 3, 131, 35, 163, 11, 139, 43, 171, 1, 129, 33, 161, 9, 137, 41, 169, }, {195, 67, 227, 99, 203, 75, 235, 107, 193, 65, 225, 97, 201, 73, 233, 105, }, {51, 179, 19, 147, 59, 187, 27, 155, 49, 177, 17, 145, 57, 185, 25, 153, }, {243, 115, 211, 83, 251, 123, 219, 91, 241, 113, 209, 81, 249, 121, 217, 89, }, {15, 143, 47, 175, 7, 135, 39, 167, 13, 141, 45, 173, 5, 133, 37, 165, }, {207, 79, 239, 111, 199, 71, 231, 103, 205, 77, 237, 109, 197, 69, 229, 101, }, {63, 191, 31, 159, 55, 183, 23, 151, 61, 189, 29, 157, 53, 181, 21, 149, }, {255, 127, 223, 95, 247, 119, 215, 87, 253, 125, 221, 93, 245, 117, 213, 85, }, }; assert(GetChannels(pBitmap) >= 3); assert(ditherAmount > 0); if (ditherAmount == 1) return; // There's nothing to do. // ditherAmount is the number of distinct values we want to be adding // to our bitmap. We want these values to range between zero and // ditherAmount - 1, so we subtract one here. ditherAmount -= 1; int NumChannels = GetChannels(pBitmap); // Before we do the dithering we have to get the values into the right // range. We then have to subtract off ditherAmount / 2 which is half of // the maximum value so that the values are centered about zero. for (int y = 0; y < DITHERSIZE; y++) { for (int x = 0; x < DITHERSIZE; x++) { int Temp = (DitherArray[y][x] * ditherAmount + 127) / 255; assert(Temp >= 0 && Temp <= ditherAmount); DitherArray[y][x] = Temp - ditherAmount / 2; } } // Now we can dither our image. for (int y = 0; y < GetHeight(pBitmap); y++) { unsigned char* pLine = GetLinePtr(pBitmap, y); int* pDitherLine = DitherArray[y & (DITHERSIZE - 1)]; for (int x = 0; x < GetWidth(pBitmap); x++) { int DitherAmount = pDitherLine[x & (DITHERSIZE - 1)]; for (int channels = 0; channels < NumChannels; channels++) { int DitheredPixel = (int)pLine[channels] + DitherAmount; if (DitheredPixel < 0) DitheredPixel = 0; if (DitheredPixel > 255) DitheredPixel = 255; pLine[channels] = DitheredPixel; } pLine += NumChannels; } } }
static void PointScaleBitmap(CSBitmap* SourceBitmap, CSBitmap* DestBitmap) { int NumChannels = GetChannels(SourceBitmap); assert(GetChannels(DestBitmap) == GetChannels(SourceBitmap)); if (gScalingOptimization) { // Point sampling. // Pre calculated with line copy of identical lines. // Should probably use new[], but I'm trying to stick to C style code. int* SrcXBuffer = (int*)malloc(sizeof(int) * GetWidth(DestBitmap)); int LastSourceY = -1; uint8_t* pLastLine = 0; if (!SrcXBuffer) return; for (int x = 0; x < GetWidth(DestBitmap); ++x) { int SourceX = (x * GetWidth(SourceBitmap) + GetWidth(SourceBitmap) / 2) / GetWidth(DestBitmap); // Make darned sure our input coordinates are valid. assert(SourceX >= 0 && SourceX < GetWidth(SourceBitmap)); SrcXBuffer[x] = SourceX * NumChannels; } // Loop over all destination lines. for (int y = 0; y < GetHeight(DestBitmap); ++y) { // I don't bother optimizing the outer loop. // Calculate the line number we want to read from. int SourceY = (y * GetHeight(SourceBitmap) + GetHeight(SourceBitmap) / 2) / GetHeight(DestBitmap); // Get the destination line pointer. uint8_t* pDestLine = GetLinePtr(DestBitmap, y); if (SourceY == LastSourceY) { // If we're still reading from the same source line then the results // are going to be identical - so just copy them over. memcpy(pDestLine, pLastLine, GetWidth(DestBitmap) * NumChannels); } else { uint8_t* pSourceLine = GetLinePtr(SourceBitmap, SourceY); // We must update pLastLine before the x-loop because the x-loop // modifies it. pLastLine = pDestLine; LastSourceY = SourceY; // For each pixel in the line... for (int x = 0; x < GetWidth(DestBitmap); ++x) { // Calculate the address of the pixel we want to read from. uint8_t* pSourcePixel = pSourceLine + SrcXBuffer[x]; // Use a switch statement instead of a loop to copy one to four bytes. // Notice the missing break statements so that it will fall through. switch (NumChannels) { case 4: pDestLine[3] = pSourcePixel[3]; case 3: pDestLine[2] = pSourcePixel[2]; case 2: pDestLine[1] = pSourcePixel[1]; case 1: pDestLine[0] = pSourcePixel[0]; } // Update our destination pointer to the next pixel. pDestLine += NumChannels; } } } free(SrcXBuffer); } else { // Point sampling. // Simplest implementation - no optimizations. // Loop over all destination lines. const unsigned int sWidth = GetWidth(SourceBitmap); const unsigned int sWidthDiv2 = sWidth / 2; const unsigned int dWidth = GetWidth(DestBitmap); for (int y = 0; y < GetHeight(DestBitmap); ++y) { // Calculate the line number we want to read from. int SourceY = (y * GetHeight(SourceBitmap) + GetHeight(SourceBitmap) / 2) / GetHeight(DestBitmap); // We don't need asserts here because GetLinePtr() will check the y-coordinate. // Get the source and destination line pointers. uint8_t* pDestLine = GetLinePtr(DestBitmap, y); uint8_t* pSourceLine = GetLinePtr(SourceBitmap, SourceY); // For each pixel in the line... for (unsigned int x = 0; x < dWidth; ++x) { // Calculate the column we want to read from. int SourceX = (x * sWidth + sWidthDiv2) / dWidth; // Make sure SourceX is in valid range. assert(SourceX >= 0 && SourceX < GetWidth(SourceBitmap)); // And loop over all of the bytes in that pixel. uint8_t* pSourcePixel = pSourceLine + SourceX * NumChannels; for (int channel = 0; channel < NumChannels; ++channel) pDestLine[channel] = pSourcePixel[channel]; pDestLine += NumChannels; } } } }
static void FilterScaleBitmap(CSBitmap* SourceBitmap, CSBitmap* DestBitmap) { const int NumSourceChannels = GetChannels(SourceBitmap); int NumSourceSkipChannels = NumSourceChannels; const int NumDestChannels = GetChannels(DestBitmap); const int kNumFilterChannels = 3; // Number of channels to filter. // Bilinear filtering. // Simple floating point code, with precalculated x-locations. double YRatio = GetHeight(SourceBitmap) / (double)GetHeight(DestBitmap); double XRatio = GetWidth(SourceBitmap) / (double)GetWidth(DestBitmap); RGBQUAD paletteRGBQUAD[256]; uint8_t bytePalette[256 * kNumFilterChannels]; if (NumSourceChannels == 1) { GetPalette(SourceBitmap, 0, 256, paletteRGBQUAD); NumSourceSkipChannels = kNumFilterChannels; // After getting the palette, copy it into a layout that is guaranteed to match that // of pixels in a bitmap. for (int i = 0; i < NUMELEMENTS(paletteRGBQUAD); ++i) { bytePalette[i * kNumFilterChannels + RED_BITMAP_OFFSET] = paletteRGBQUAD[i].rgbRed; bytePalette[i * kNumFilterChannels + GREEN_BITMAP_OFFSET] = paletteRGBQUAD[i].rgbGreen; bytePalette[i * kNumFilterChannels + BLUE_BITMAP_OFFSET] = paletteRGBQUAD[i].rgbBlue; } } // Optimized version of filtered scaling. // Should probably use new[], but I'm trying to stick to C style code. float* SrcXBuffer = (float*)malloc(sizeof(float) * GetWidth(DestBitmap)); if (!SrcXBuffer) return; // Precalculate the sourceX values, including left edge clipping them. for (int x = 0; x < GetWidth(DestBitmap); ++x) { double SourceX = (x + 0.5) * XRatio; assert(SourceX >= 0 && SourceX < GetWidth(SourceBitmap)); SourceX -= 0.5; // Adjust into a more convenient range. Now represents // the left edge of the pixel. // Deal with the unfortunate border cases. We are clamping the pixels. // Other possibilities include wrapping, or using a border colour. if (SourceX < 0) SourceX = 0; if (SourceX >= GetWidth(SourceBitmap) - 1) SourceX = GetWidth(SourceBitmap) - 1; SrcXBuffer[x] = (float)SourceX; } for (int y = 0; y < GetHeight(DestBitmap); ++y) { double SourceY = (y + 0.5) * YRatio; assert(SourceY >= 0 && SourceY < GetHeight(SourceBitmap)); SourceY -= 0.5; // Adjust into a more convenient range. Now represents // the left edge of the pixel. // Deal with the unfortunate border cases. We are clamping the pixels. // Other possibilities include wrapping, or using a border colour. if (SourceY < 0) SourceY = 0; if (SourceY > GetHeight(SourceBitmap) - 1) SourceY = GetHeight(SourceBitmap) - 1; // If SourceY might be negative you have to use floor() instead of just casting to int. int FirstLine = (int)SourceY; double SecondLineWeight = SourceY - FirstLine; // We don't need asserts here because GetLinePtr() will check the y-coordinate. uint8_t* pDestLine = GetLinePtr(DestBitmap, y); uint8_t* pSourceLine1 = GetLinePtr(SourceBitmap, FirstLine); // Make sure we don't try getting the address of a non-existent line. // Make sure this always gets initialized to something. uint8_t* pSourceLine2 = pSourceLine1; if (FirstLine + 1 < GetHeight(SourceBitmap)) pSourceLine2 = GetLinePtr(SourceBitmap, FirstLine + 1); uint8_t* pSourcePixel1Left; // Top line, left pixel. uint8_t* pSourcePixel2Left; // Second line, left pixel. for (int x = 0; x < GetWidth(DestBitmap); ++x) { double SourceX = SrcXBuffer[x]; int FirstPixel = int(SourceX); // Set up pointers into the bitmap or the palette, for the left sample pixels. if (NumSourceChannels == 1) { pSourcePixel1Left = bytePalette + pSourceLine1[FirstPixel] * kNumFilterChannels; pSourcePixel2Left = bytePalette + pSourceLine2[FirstPixel] * kNumFilterChannels; } else { pSourcePixel1Left = pSourceLine1 + FirstPixel * NumSourceChannels; pSourcePixel2Left = pSourceLine2 + FirstPixel * NumSourceChannels; } uint8_t* pSourcePixel1Right; // Top line, right pixel. uint8_t* pSourcePixel2Right; // Second line, right pixel. // Now setup pointers into the bitmap or the palette, for the right // sample pixels. If there are no right sample pixels (right edge) // then point at the left sample pixels. if (SourceX >= GetWidth(SourceBitmap) - 1) { pSourcePixel1Right = pSourcePixel1Left; pSourcePixel2Right = pSourcePixel2Left; } else { if (NumSourceChannels == 1) { pSourcePixel1Right = bytePalette + pSourceLine1[FirstPixel+1] * kNumFilterChannels; pSourcePixel2Right = bytePalette + pSourceLine2[FirstPixel+1] * kNumFilterChannels; } else { pSourcePixel1Right = pSourcePixel1Left + NumSourceChannels; pSourcePixel2Right = pSourcePixel2Left + NumSourceChannels; } } double SecondPixelWeight = SourceX - FirstPixel; for (int channel = 0; channel < kNumFilterChannels; ++channel) { double FirstLine = pSourcePixel1Left[channel] + (pSourcePixel1Right[channel] - pSourcePixel1Left[channel]) * SecondPixelWeight; double SecondLine = pSourcePixel2Left[channel] + (pSourcePixel2Right[channel] - pSourcePixel2Left[channel]) * SecondPixelWeight; pDestLine[channel] = (uint8_t)(FirstLine + (SecondLine - FirstLine) * SecondLineWeight); } pDestLine += NumDestChannels; } } free(SrcXBuffer); }