 |
|
 |
Finally, someone else has discovered the use of GetBits()! I found this some time ago and was thinking of writing an article too, because I couldn't understand why everyone was still using GetPixel() and complaining about how slow it is. So I'm glad you wrote something on this.
Just an aside, to make image manipulations even faster and for easier coding, after I load the image file I convert it to a top-down DIB. This way your image manipulation functions (that modify all pixels) only need one loop that iterates GetWidth*GetHeight times. Also, of course, don't put a call to GetWidth() and GetHeight() in a loop to call repeatedly, use variables to hold the values.
To convert to top-down, just copy all the pixels into a temp buffer using GetPitch() or GetPixelAddress(), do a .Destroy() call to release the CImage, then do a .Create(...) call with a negative height value to make the new top-down image, and copy the temp buffer into it with a call to memcpy_s(...). I also make it 32 bit so the GetBits() pointer can be an int.
Manipulations are so fast I use slider buttons in some functions to modify the image and watch the image display change in near real-time.
Thanks,
Darryl
|
|
|
|
 |
|
|
 |
|
 |
Ial come at your place and kill ur cat LUL.
|
|
|
|
 |
|
 |
How about Bitmap::LockBits method?
Seems it is faster
geoyar
|
|
|
|
 |
|
 |
Sounds like it gets the pointer to some color table. Shouldn't bring great difference to the entire program since it is required to be called once.
|
|
|
|
 |
|
 |
It do brings a difference.
It transfers an area of the bitmap to the memory. Actually it gives to you a memory array. And reading/writing from/to memory is faster (so people are saying.)
In Gdiplus it is
Status Bitmap::LockBits(const Rect *rect,
UINT flags,
PixelFormat format,
BitmapData *lockedBitmapData
);
You can ask for any pixel format legal in Gdiplus .
In MFC it is
DWORD CBitmap::GetBitmapBits(
DWORD dwCount,
LPVOID lpBits
) const;
Copies the bit pattern of the CBitmap object into the buffer that is pointed to by lpBits
It is less useful, but again, after calling it you are facing PC memory.
I think something like it exists in Win SDK too.
geoyar
|
|
|
|
 |
|
 |
The cpu caching algorithms are optimised for sequential use. It's very complicated. Take my word for it.
Your loops will almost always be much faster if the outer loop is vertical, and the inner loop horizontal, so that the inner loop accesses memory sequentially
e.g.:
for (int y = 0; y < height; ++y)
for (int x = 0; x < width; ++x)
{
//do something with x,y
}
or:
for (int y = 0; y < height; ++y)
{
p = startOfLine(y);
for (int x = 0; x < width; ++x)
{
//do something with *p
p = nextPixel(p); // or p+=bytesPerPixel if 1==sizeof(*p), or ++p if bytesPerPixel==sizeof(*p)
}
}
making width and height variables will sometimes allow the compiler to generate better code
|
|
|
|
 |
|
|
 |
|
 |
Geez thnx for the advice.
|
|
|
|
 |
|
 |
By the way in my previous failed job of video stream processing I did the vertical image processing since it required to mix pixel colors with the couple of bottom pixlz. But the resulting CPU load was 2x25% for double blitting and 1x12% for image processing = 62%. My client wants 10%.
|
|
|
|
 |
|
 |
Since it helps newps like me and they send me letterz with thnxez ^_^. Thank you.
|
|
|
|
 |
|
 |
Please could you indicate how much quicker doing direct arithmetic is over using the routines (e.g. by timing conversions of large bitmaps) so that people can see what an improvement your technique has.
Does your method work with compressed bitmaps?
|
|
|
|
 |
|
 |
Hi, it's approx 1000 times faster than GetPixel and SetPixel crap. Unfortunately I have no idea how to deal with JPEGs and stuff, but I am sure there are fast API functions to decompress a JPG into bitmap and then deal with color table. If you find some please can you post them I shall add them to teh article laterz. THNX
By the way the code and the project attached is just an example to compare these two methods. If you build it and launch in conjunction with a C:\\1.bmp file (of large size) you can see by yourself the difference. At my computer (3GHZ P4) an image of 800x600 was processed through GetPixel and SetPixel for 3seconds. And through direct arithmetics it was done for 0.01 seconds or so.
|
|
|
|
 |
|
 |
I think you would struggle with JPEG, but PNG and compressed BMP / DIB is doable. I don't use C++ so am unsure of what the CImage class gives you.
I started writing a JavaScript module for dynamic creation / editing of BMPs, but other tasks have pushed it down my stack of things to do so it is incomplete and not fully debugged. FWIW, here is the extract from it for handling BMPs, including compressed ones (you can get the BMP file structure and then convert that into C++ syntax):
// -------------------------------------------------------------------------
//
// Load a Windows Bitmap format image
function Picture.prototype.fromBMP(text)
{
// See Picture.prototype.toBMP() for layout details
if (text.length == 0)
return this.error.raise(Picture.E_FROMBMP_NOTEXT);
else if ( text.substring(0, 2) != || text.toNumber4R(2) <= 54
|| text.length != text.toNumber4R(2)
)
return this.error.raise(Picture.E_FROMBMP_BADSIGNATURE);
// Break the text into its components
var width = text.toNumber4R(18);
var height = text.toNumber4R(22);
var depth = text.toNumber2R(28); // Depth (1, 4, 8, or 24)
var compression = text.charCodeAt(30); // 0 = off, 1 = 8 bits RLE, 2 = 4 bits RLE
var imageMapLength = text.toNumber4R(34);
var plen = text.toNumber4R(46); // No of colours in the palette (0 for depth 24)
if (plen == 0 && depth < 24) // 0 implies full palette at this depth
plen = 1 << depth;
var plen4 = plen * 4;
var imageMap = text.substring(54 + plen4); // Compressed image
// Palette
var palette = new Array();
for (var i = 0; i < plen4; i += 4)
palette[i >> 2] = text.toNumber3R(54 + i); // 4 byte field but only first 3 bytes are significant
// Create the internal image elements
this.canvas = new Array();
this.crop(1, width, 1, height);
this.moveTo(1, 1);
this.foregroundColour = plen < 1 ? this.foregroundColour : palette[0];
this.backgroundColour = plen < 2 ? this.backgroundColour : palette[1];
// Parse the image map
switch (depth)
{
case 1: // Monochrome
var paddedWidth = ((width + 31) >> 5) << 2; // No of allocated bytes in each row
for (var row = 0; row < height; row++)
{
var rowStartOffset = row * paddedWidth;
for (var col = 0; col < width; col += 8)
{
var pByte = imageMap.charCodeAt(rowStartOffset + col / 8);
for (var i = 0; i < 8; i++) // Bits within the byte
if ((pByte & (1 << i)) == 0) // Foreground pixel
this.pixel(col + 8 - i, row + 1, this.foregroundColour);
} // for
} // for
break;
case 4: // 16 colours, two palette indices per byte
if (compression)
{
var row = 1;
var col = 1;
var ix = 0; // Index in the image map
while (ix < imageMap.length)
{
var i = imageMap.charCodeAt(ix++);
if (i == 00) // Special value
{
i = imageMap.charCodeAt(ix++);
switch (i)
{
case 00: // 00 00 = end of line
col = 0;
row++;
break;
case 01: // 00 01 = end of bitmap
ix = imageMap.length;
break;
case 02: // 00 02 xx yy = go to X+xx, Y+yy
row += imageMap.charCodeAt(ix++);
col += imageMap.charCodeAt(ix++);
break;
default: // 00 nn <(nn+1)/2 bytes>
// nn 1/2 word palette indices
// (pad to word boundary with 00 if necessary)
for (var j = 0; j < i; j += 2)
{
var pByte = imageMap.charCodeAt(ix + j / 2);
this.pixel(++col, row + 1, palette[pByte >> 4]); // Left nibble
if (j + 1 < i)
this.pixel(++col, row + 1, palette[pByte & 0xF]); // Right nibble
} // for
ix += (i + 1) > 1;
} // switch
} // if
else // nn ab = Repeat pixels with palette indices a and b for nn pixels
{
var pByte = imageMap.charCodeAt(ix++);
var a = palette[pByte >> 4]; // Left nibble
var b = palette[pByte & 0xF]; // Right nibble
for (var j = 0; j < i; j += 2)
{
this.pixel(++col, row + 1, a);
if (j + 1 < i)
this.pixel(++col, row + 1, b);
} // for
} // else
} // while
} // if
else // Uncompressed
{
var paddedWidth = ((width + 7) >> 3) << 2; // No of allocated bytes in each row
for (var row = 0; row < height; row++)
{
var rowStartOffset = row * paddedWidth;
for (var col = 0; col < width; col += 2)
{
var pByte = imageMap.charCodeAt(rowStartOffset + col / 2);
this.pixel(col + 1, row + 1, palette[pByte >> 4]); // Left nibble
this.pixel(col + 2, row + 1, palette[pByte & 0xF]); // Right nibble
} // for
} // for
} // else
break;
case 8: // 256 colours, one palette index per byte
if (compression)
{
var row = 1;
var col = 1;
var ix = 0; // Index in the image map
while (ix < imageMap.length)
{
var i = imageMap.charCodeAt(ix++);
if (i == 00) // Special value
{
i = imageMap.charCodeAt(ix++);
switch (i)
{
case 00: // 00 00 = end of line
col = 0;
row++;
break;
case 01: // 00 01 = end of bitmap
ix = imageMap.length;
break;
case 02: // 00 02 xx yy = go to X+xx, Y+yy
row += imageMap.charCodeAt(ix++);
col += imageMap.charCodeAt(ix++);
break;
default: // 00 nn <nn bytes> = sequence (pad with 00 to word boundary)
for (var j = 0; j < i; j++)
this.pixel(col++, row, palette[imageMap.charCodeAt(ix++)]);
if (i & 0x1) // Padding needed?
ix++;
} // switch
} // if
else // nn xx = repeat palette index xx for nn pixels
{
var xx = imageMap.charCodeAt(ix++);
if (xx) // Background colour?
for (var j = 0; j < i; j++)
this.pixel(col++, row, palette[xx]);
else // It is the background colour
col += i;
} // else
} // while
} // if
else // Uncompressed
{
var paddedWidth = (width + 3) & 0xFFFFFF8; // Pad to 4-byte boundary
for (var row = 0; row < height; row++)
{
var rowStartOffset = row * paddedWidth;
for (var col = 0; col < width; col++)
this.pixel(col + 1, row + 1, palette[imageMap.charCodeAt(rowStartOffset + col)]);
} // for
} // else
break;
case 24: // 2**24 colours, one byte in 3 pixels (Green, Blue, Red)
for (var row = 0; row < height; row++)
{
var rowStartOffset = row * height * 3;
for (var col = 0; col <= width; col++)
this.pixel(col + 1, row + 1, imageMap.toNumber3R(rowStartOffset + col * 3));
} // for
break;
default:
this.error.raise(Picture.E_FROMBMP_UNSUPPORTED, width, height, depth, compression, plen, imageMap.length);
} // switch
} // Picture.prototype.fromBMP
//
Picture.error(
);
//
// -------------------------------------------------------------------------
//
// Convert the canvas to BMP (Windows Bitmap) format
function Picture.prototype.toBMP()
{
// Bitmaps can be
// Depth = 1 Max colours = 2
// Depth = 4 Max colours = 16 *** Not included here to speed up conversions ***
// Depth = 8 Max colours = 256
// Depth = 24 Max colours = 16777216
// Compressions (RLE-8 (for 256 colours) / RLE-4 (for 16 colours))
// 00 00 Both End of line
// 00 01 Both End of bitmap
// 00 02 xx yy Both Go to X+xx, Y+yy (where X,Y is current location)
//
// 00 nn <nn bytes> 00 RLE-8 nn palette indices (pad to word boundary with 00 if necessary) 03 <= nn <= FF
// nn xx RLE-8 Repeat palette index xx for nn pixels (nn > 0)
//
// 00 nn <(nn+1)/2 bytes> RLE-4 nn 1/2 word palette indices (pad to word boundary with 00 if necessary). 03 <= nn <= FF
// nn ab RLE-4 Repeat pixels with palette indices a and b for nn pixels (nn > 0)
// Expand text to double word boundary
var pad =
new Function(
"text",
"return text.length % 4 == 0 " +
"? text " +
": text + '\\0\\0\\0\\0'.substring(text.length % 4); "
);
// Create the palette
this.palette = new Array(this.foregroundColour, this.backgroundColour);
this.palette.maxLen = 256; // Extension property: Max no of entries permitted in the palette
this.palette.findNearest = false; // Extension property: Select nearest matching colour if the palette is full?
var bgColour24 = this.backgroundColour.toBinary3R(); // Background colour as used in 24 bit depth bitmaps
// Basic values
var c = this.crop(); // Get dimensions
var imageMap24 = var imageMap8 = var imageMap1 = var emptyRowMap24 = pad(bgColour24.repeat(this.width));
var emptyRowMap8 = Picture.toBMP_RLE8_compressRepeated( var emptyRowMap1 = pad(
// Scan the canvas (Note: Scan is from bottom to top as bitmaps are for (var row = c.minY; row <= c.maxY; row++)
{
// Get row details
var crow = this.canvas[row];
var plen = this.palette.length; // No of colours in the palette
if (crow && crow.minX <= c.maxX && crow.maxX >= c.minX) // Row defined and has significant content
{
// Row is defined. Get length before first significant pixel / after last significant pixel
var firstPixel = crow.minX < c.minX ? c.minX : crow.minX;
var lastPixel = crow.maxX > c.maxX ? c.maxX : crow.maxX;
var gapLeft = firstPixel - c.minX;
var gapRight = c.maxX - lastPixel;
if (plen > 256) // Only 24 bit bitmap wanted
{
var rowMap24 = for (var col = firstPixel; col <= lastPixel; col++)
rowMap24 +=
typeof crow[col] ==
imageMap24 +=
pad(
(gapLeft ? bgColour24.repeat(gapLeft) : rowMap24 +
(gapRight ? bgColour24.repeat(gapRight) : );
} // if
else // 24, 8 and (possibly) 1 bit bitmap(s) wanted
{
var rowMap24 = var rowMap8 = var rowMap1 = var rowMap1BitNo = 7 - (gapLeft & 0x7); // Next bit to overwrite in rowMap1Byte (76543210)
var rowMap1Byte = (0xFF << (rowMap1BitNo + 1)) & 0xFF; // Set start bits to background pixel palette index (always 1)
for (var col = firstPixel; col <= lastPixel; col++)
{
var pixel = typeof crow[col] == rowMap24 += pixel.toBinary3R();
var index = this.paletteIndex(pixel);
rowMap8 += String.fromCharCode(index);
if (plen <= 2) // Add to monochrome bitmap
if (rowMap1BitNo)
rowMap1Byte |= index << rowMap1BitNo--;
else // At end of byte
{
rowMap1 += String.fromCharCode(rowMap1Byte + index);
rowMap1Byte = 0;
rowMap1BitNo = 7;
} // else
} // for
// Build row from components and add to image maps
imageMap24 +=
pad(
(gapLeft ? bgColour24.repeat(gapLeft) : rowMap24 +
(gapRight ? bgColour24.repeat(gapRight) : );
imageMap8 += // No padding required. Background colour palette index is always 1
(gapLeft ? Picture.toBMP_RLE8_compressRepeated( Picture.toBMP_RLE8_compress(rowMap8) +
(gapRight ? Picture.toBMP_RLE8_compressRepeated(
if (plen <= 2) // Monochrome bitmap
{
while (gapRight) // Add trailing background pixels
if (rowMap1BitNo) // Background pixel palette index is always 1
{
rowMap1Byte |= 0x01 << rowMap1BitNo--;
gapRight--;
} // if
else // At end of byte
{
rowMap1 += String.fromCharCode(rowMap1Byte + 0x01);
gapRight--;
rowMap1Byte = 0;
rowMap1BitNo = 7;
if (gapRight >= 8) // Add whole bytes of background pixel palette indices
{
rowMap1 += gapRight &= 0x7;
} // if
} // else
imageMap1 += // Background colour palette index is always 1
pad(
(gapLeft > 7 ? rowMap1 +
(rowMap1BitNo == 7 ? );
} // if
} // else
} // if
else // Undefined row
{
imageMap24 += emptyRowMap24;
if (plen <= 256)
{
imageMap8 += emptyRowMap8;
if (plen <= 2)
imageMap1 += emptyRowMap1;
} // if
} // else
} // for
// Get RGBQUADs for the paletted colours
var paletteMap = var plen = this.palette.length;
var depth = plen > 256 ? 24 : (plen > 2 ? 8 : 1);
if (plen <= 256)
{
imageMap24 = depth == 8 ? imageMap8 : imageMap1;
for (var i = 0; i < plen; i++)
paletteMap += (this.palette[i] & 0x00FFFFFF).toBinary4R();
} // if
// Build components into the BMP file layout
// Win3DIBFile = BitmapFileHeader + BitmapInfoHeader + Win3ColorTable + Image
return (
// BitmapFileHeader (14 bytes)
(14 + 40 + paletteMap.length + imageMap24.length).toBinary4R() + // Size
(14 + 40 + paletteMap.length).toBinary4R() + // OffsetBits
// BitmapInfoHeader (40 bytes)
this.width.toBinary4R() +
this.height.toBinary4R() +
"\1\0" + // Planes (1) +
depth.toBinary2R() + // BitCount
(depth == 8 ? imageMap24.length.toBinary4R() + // SizeImage
(paletteMap.length / 4).toBinary4R() + // ColorsUsed (0 for 24 bit)
// Win3ColorTable
paletteMap +
// Image
imageMap24
);
} // Picture.prototype.toBMP
//
// -------------------------------------------------------------------------
//
// Support functions for Picture.prototype.toBMP
//
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
// Compress an 8-bit BMP pixel stream
/* private */ function Picture.toBMP_RLE8_compress(text)
{
var result = if (text) // Ignore empty stream
{
// Split into segments and repetitions
var prevch = text.charAt(0);
var repeatStart = 0;
var segStart = 0;
var tLen = text.length;
for (var i = 1; i < tLen; i++)
{
var ch = text.charAt(i);
if (ch != prevch)
{
if (i > repeatStart + 2) // End of a repeated section?
{
result +=
( segStart == repeatStart
? : Picture.toBMP_RLE8_compressSegment(text.substring(segStart, repeatStart))
) +
Picture.toBMP_RLE8_compressRepeated(prevch, i - repeatStart);
segStart = i;
} //if
// Start a new repetition block
prevch = ch;
repeatStart = i;
} // if
} // for
// Output tail end
if (tLen > repeatStart + 2) // End of a repeated section?
result +=
( segStart == repeatStart
? : Picture.toBMP_RLE8_compressSegment(text.substring(segStart, repeatStart))
) +
Picture.toBMP_RLE8_compressRepeated(prevch, tLen - repeatStart);
else // Not in a repetition
result += Picture.toBMP_RLE8_compressSegment(text.substring(segStart));
} // if
return result;
} // Picture.toBMP_RLE8_compress
//
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
// Compress a repeated pixel in an 8-bit BMP pixel stream
/* private */ function Picture.toBMP_RLE8_compressRepeated(pixel, count)
{
// Note: Pixel is palette index as a character
// Max of 255 for repetition count
var result = while (count >= 255)
{
result += count -= 255;
} // while
// Add any tail
return result + (count ? String.fromCharCode(count) + pixel : } // Picture.toBMP_RLE8_compressRepeated
//
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
// Output a segment of palette indices in an 8-bit BMP pixel stream
/* private */ function Picture.toBMP_RLE8_compressSegment(text)
{
// Max of 255 bytes needs extra padding byte, so only use 254 bytes (no padding needed)
var result = while (text.length > 254)
{
result += text = text.substring(254);
} // while
// Add tail
switch (text.length)
{
case 0: // Nothing left to do
break;
case 1: // One pixel left
result += break;
case 2: // Two pixels left
result += break;
default: // 3 to 254 pixels (Pad if odd no of bytes)
result += } // switch
return result;
} // Picture.toBMP_RLE8_compressSegment
//
// -------------------------------------------------------------------------
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
// Convert a binary string to a number (LSB, ..., MSB)
String.prototype.toNumber2R =
new Function(
"optStartOffset", // Optional start location. Default: 0. Saves having to get substrings
"var i = optStartOffset ? optStartOffset : 0; " +
"return (this.charCodeAt(i + 1) << 8) | this.charCodeAt(i); "
);
//
String.prototype.toNumber3R =
new Function(
"optStartOffset", // Optional start location. Default: 0. Saves having to get substrings
"var i = optStartOffset ? optStartOffset : 0; " +
"return (this.charCodeAt(i + 2) << 16) | (this.charCodeAt(i + 1) << 8) | this.charCodeAt(i); "
);
//
String.prototype.toNumber4R =
new Function(
"optStartOffset", // Optional start location. Default: 0. Saves having to get substrings
"var i = optStartOffset ? optStartOffset : 0; " +
"return (this.charCodeAt(i + 3) << 24) | (this.charCodeAt(i + 2) << 16) | (this.charCodeAt(i + 1) << 8) | this.charCodeAt(i); "
);
//
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
// Convert a number to a binary string (LSB, ..., MSB)
Number.prototype.toBinary2R = new Function("return String.fromCharCode(this & 0xFF, (this & 0xFF00) >> 8); ");
Number.prototype.toBinary3R = new Function("return String.fromCharCode(this & 0xFF, (this & 0xFF00) >> 8, (this & 0xFF0000) >> 16); ");
Number.prototype.toBinary4R = new Function("return String.fromCharCode(this & 0xFF, (this & 0xFF00) >> 8, (this & 0xFF0000) >> 16, (this & 0xFF000000) >>> 24); ");
//
|
|
|
|
 |
|
 |
looks like this code creates an image of requested format.
I wish I could know JavaScript as kewl as you do...
You see, I used windows API functions that worked slow. Then I used ptr arithmetics. You use browser functions (that is thru javascript). I don't even have an idea how to use pointers (to computer's memory) in javascript. If there is that way, I think I'd be able to help you.
By the way if you use images to allow people draw with pallete or pencil tools you shouldn't experience performance troubles unless some stupid bug in your code.
|
|
|
|
 |
|
 |
Phew, some harsh commenting!
Although I have used a similar technique myself, I still believe it is worthy of being read by people who are new to programming/imaging.
Thank you for the effort! 3/5 from me.
|
|
|
|
 |
|
 |
Yupp I also think so. Maybe far not an article but I hope google shall scan it and tell people a solution on "Omg why my graphic program is so freaking slow?".
PS: First pancake is always a roll hehe.
|
|
|
|
 |
|
 |
Two things:
As others have already pointed out, this is not an article, but rather a code dump. An article would be, for example, an explanation of how the bitmap is stored, and how to access its pixels directly, with some example code. But a code listing does not make an article.
Second, and more important, your code is wrong. You are accessing the pixels as if they were stored as RGB, but they are stored the other way around, as BGR. The example you provide seems to work because you are changing the green channel, which is in the middle in both cases, but if you change the Red or Blue channels, you'll get different results with the two methods.
For an example of use of the same technique, with the same error, by the way, in codeproject: Converting Color to Grayscale Using ATL::CImage[^]
|
|
|
|
 |
|
 |
Hey mluri thank for your comment, you are right. I forgot to say that ptr+0 is a B color, ptr+1 is a G color, ptr+2 is an R color. Thank you for your help, I hope people shall read your post or find out by themselves bout that.
modified on Tuesday, June 8, 2010 4:37 AM
|
|
|
|
 |
|
 |
Well sorry that's my first time, I've got screwed with the javascriptz text editor since it put large gaps between lines instead of posting plain text. I shall pay more attention previewing and modyfying the result next time.
Regarding dummies, I spent couple of weeks searching for solutions but all I had were just lockbits and DrawDibs functions without any physical sense of the problem. And the sense is how the bitmap data is located. Microsoft made it hidden within HDCs and HBITMAPS. That's what I explained in this so called article.
And by the way I don't need any code to be posted as a reply, this stuff works well and It was written as an example to show the difference between GetPixels-like API functions and the pointer arithmetics method. I doubt that there can be a practical use of this code (not the method).
And by the way Opera browser does not support the creation of articles but the formatting is well.
PS: I am trying to get close to people who knows programming well for more than 2years but noone in our damned land has any idea bout programming and noone can teach us. Just few of us speak english. Most stuff we learned from MSDN and codeproject, however the method I described I couldn't find anywhere including this site. Maybe coz it is "for dummies", but a lor of such the articles I saw here. And they helped me a lot. I hope my so called article shall help someone too.
modified on Saturday, June 5, 2010 9:22 AM
|
|
|
|
 |
|
 |
If you want to see how this is really done, search the articles for "Image processing for dummies".
You really haven't written an article. You've supplied a code dump with the hope that comments in the code will write it for you. That never works.
|
|
|
|
 |
|
 |
Thank you, sir, coz it's my first article here. Now I kno how to fix it.
|
|
|
|
 |
|
 |
0) Your title is too long
1) Your formatting is screwed up
2) It's just a code dump
3) It appears as if this would make a better tip/trick than it does an article.
.45 ACP - because shooting twice is just silly ----- "Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997 ----- "The staggering layers of obscenity in your statement make it a work of art on so many levels." - J. Jystad, 2001
|
|
|
|
 |