+ long pixel_idx;
+ const unsigned char *src;
+ unsigned char *dst;
+
+ // Calculate the average of all pixels in the blur radius for the first
+ // pixel of the row
+ for ( int kernel_x = -blurRadius; kernel_x <= blurRadius; kernel_x++ )
+ {
+ // To deal with the pixels at the start of a row so it's not
+ // grabbing GOK values from memory at negative indices of the
+ // image's data or grabbing from the previous row
+ if ( kernel_x < 0 )
+ pixel_idx = y * M_IMGDATA->m_width;
+ else
+ pixel_idx = kernel_x + y * M_IMGDATA->m_width;
+
+ src = src_data + pixel_idx*3;
+ sum_r += src[0];
+ sum_g += src[1];
+ sum_b += src[2];
+ if ( src_alpha )
+ sum_a += src_alpha[pixel_idx];
+ }
+
+ dst = dst_data + y * M_IMGDATA->m_width*3;
+ dst[0] = (unsigned char)(sum_r / blurArea);
+ dst[1] = (unsigned char)(sum_g / blurArea);
+ dst[2] = (unsigned char)(sum_b / blurArea);
+ if ( src_alpha )
+ dst_alpha[y * M_IMGDATA->m_width] = (unsigned char)(sum_a / blurArea);
+
+ // Now average the values of the rest of the pixels by just moving the
+ // blur radius box along the row
+ for ( int x = 1; x < M_IMGDATA->m_width; x++ )
+ {
+ // Take care of edge pixels on the left edge by essentially
+ // duplicating the edge pixel
+ if ( x - blurRadius - 1 < 0 )
+ pixel_idx = y * M_IMGDATA->m_width;
+ else
+ pixel_idx = (x - blurRadius - 1) + y * M_IMGDATA->m_width;
+
+ // Subtract the value of the pixel at the left side of the blur
+ // radius box
+ src = src_data + pixel_idx*3;
+ sum_r -= src[0];
+ sum_g -= src[1];
+ sum_b -= src[2];
+ if ( src_alpha )
+ sum_a -= src_alpha[pixel_idx];
+
+ // Take care of edge pixels on the right edge
+ if ( x + blurRadius > M_IMGDATA->m_width - 1 )
+ pixel_idx = M_IMGDATA->m_width - 1 + y * M_IMGDATA->m_width;
+ else
+ pixel_idx = x + blurRadius + y * M_IMGDATA->m_width;
+
+ // Add the value of the pixel being added to the end of our box
+ src = src_data + pixel_idx*3;
+ sum_r += src[0];
+ sum_g += src[1];
+ sum_b += src[2];
+ if ( src_alpha )
+ sum_a += src_alpha[pixel_idx];
+
+ // Save off the averaged data
+ dst = dst_data + x*3 + y*M_IMGDATA->m_width*3;
+ dst[0] = (unsigned char)(sum_r / blurArea);
+ dst[1] = (unsigned char)(sum_g / blurArea);
+ dst[2] = (unsigned char)(sum_b / blurArea);
+ if ( src_alpha )
+ dst_alpha[x + y * M_IMGDATA->m_width] = (unsigned char)(sum_a / blurArea);
+ }
+ }
+
+ return ret_image;
+}
+
+// Blur in the vertical direction
+wxImage wxImage::BlurVertical(int blurRadius) const
+{
+ wxImage ret_image(MakeEmptyClone());
+
+ wxCHECK( ret_image.IsOk(), ret_image );
+
+ const unsigned char* src_data = M_IMGDATA->m_data;
+ unsigned char* dst_data = ret_image.GetData();
+ const unsigned char* src_alpha = M_IMGDATA->m_alpha;
+ unsigned char* dst_alpha = ret_image.GetAlpha();
+
+ // number of pixels we average over
+ const int blurArea = blurRadius*2 + 1;
+
+ // Vertical blurring algorithm - same as horizontal but switched the
+ // opposite direction
+ for ( int x = 0; x < M_IMGDATA->m_width; x++ )
+ {
+ // Variables used in the blurring algorithm
+ long sum_r = 0,
+ sum_g = 0,
+ sum_b = 0,
+ sum_a = 0;
+
+ long pixel_idx;
+ const unsigned char *src;
+ unsigned char *dst;
+
+ // Calculate the average of all pixels in our blur radius box for the
+ // first pixel of the column
+ for ( int kernel_y = -blurRadius; kernel_y <= blurRadius; kernel_y++ )
+ {
+ // To deal with the pixels at the start of a column so it's not
+ // grabbing GOK values from memory at negative indices of the
+ // image's data or grabbing from the previous column
+ if ( kernel_y < 0 )
+ pixel_idx = x;
+ else
+ pixel_idx = x + kernel_y * M_IMGDATA->m_width;
+
+ src = src_data + pixel_idx*3;
+ sum_r += src[0];
+ sum_g += src[1];
+ sum_b += src[2];
+ if ( src_alpha )
+ sum_a += src_alpha[pixel_idx];
+ }
+
+ dst = dst_data + x*3;
+ dst[0] = (unsigned char)(sum_r / blurArea);
+ dst[1] = (unsigned char)(sum_g / blurArea);
+ dst[2] = (unsigned char)(sum_b / blurArea);
+ if ( src_alpha )
+ dst_alpha[x] = (unsigned char)(sum_a / blurArea);
+
+ // Now average the values of the rest of the pixels by just moving the
+ // box along the column from top to bottom
+ for ( int y = 1; y < M_IMGDATA->m_height; y++ )
+ {
+ // Take care of pixels that would be beyond the top edge by
+ // duplicating the top edge pixel for the column
+ if ( y - blurRadius - 1 < 0 )
+ pixel_idx = x;
+ else
+ pixel_idx = x + (y - blurRadius - 1) * M_IMGDATA->m_width;
+
+ // Subtract the value of the pixel at the top of our blur radius box
+ src = src_data + pixel_idx*3;
+ sum_r -= src[0];
+ sum_g -= src[1];
+ sum_b -= src[2];
+ if ( src_alpha )
+ sum_a -= src_alpha[pixel_idx];
+
+ // Take care of the pixels that would be beyond the bottom edge of
+ // the image similar to the top edge
+ if ( y + blurRadius > M_IMGDATA->m_height - 1 )
+ pixel_idx = x + (M_IMGDATA->m_height - 1) * M_IMGDATA->m_width;
+ else
+ pixel_idx = x + (blurRadius + y) * M_IMGDATA->m_width;
+
+ // Add the value of the pixel being added to the end of our box
+ src = src_data + pixel_idx*3;
+ sum_r += src[0];
+ sum_g += src[1];
+ sum_b += src[2];
+ if ( src_alpha )
+ sum_a += src_alpha[pixel_idx];
+
+ // Save off the averaged data
+ dst = dst_data + (x + y * M_IMGDATA->m_width) * 3;
+ dst[0] = (unsigned char)(sum_r / blurArea);
+ dst[1] = (unsigned char)(sum_g / blurArea);
+ dst[2] = (unsigned char)(sum_b / blurArea);
+ if ( src_alpha )
+ dst_alpha[x + y * M_IMGDATA->m_width] = (unsigned char)(sum_a / blurArea);
+ }
+ }
+
+ return ret_image;
+}
+
+// The new blur function
+wxImage wxImage::Blur(int blurRadius) const
+{
+ wxImage ret_image;
+ ret_image.Create(M_IMGDATA->m_width, M_IMGDATA->m_height, false);
+
+ // Blur the image in each direction
+ ret_image = BlurHorizontal(blurRadius);
+ ret_image = ret_image.BlurVertical(blurRadius);
+
+ return ret_image;
+}
+
+wxImage wxImage::Rotate90( bool clockwise ) const
+{
+ wxImage image(MakeEmptyClone(Clone_SwapOrientation));
+
+ wxCHECK( image.IsOk(), image );
+
+ long height = M_IMGDATA->m_height;
+ long width = M_IMGDATA->m_width;
+
+ if ( HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_X) )
+ {
+ int hot_x = GetOptionInt( wxIMAGE_OPTION_CUR_HOTSPOT_X );
+ image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y,
+ clockwise ? hot_x : width - 1 - hot_x);
+ }
+
+ if ( HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y) )
+ {
+ int hot_y = GetOptionInt( wxIMAGE_OPTION_CUR_HOTSPOT_Y );
+ image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_X,
+ clockwise ? height - 1 - hot_y : hot_y);
+ }
+
+ unsigned char *data = image.GetData();
+ unsigned char *target_data;
+
+ // we rotate the image in 21-pixel (63-byte) wide strips
+ // to make better use of cpu cache - memory transfers
+ // (note: while much better than single-pixel "strips",
+ // our vertical strips will still generally straddle 64-byte cachelines)
+ for (long ii = 0; ii < width; )
+ {
+ long next_ii = wxMin(ii + 21, width);
+
+ for (long j = 0; j < height; j++)
+ {
+ const unsigned char *source_data
+ = M_IMGDATA->m_data + (j*width + ii)*3;
+
+ for (long i = ii; i < next_ii; i++)
+ {
+ if ( clockwise )
+ {
+ target_data = data + ((i + 1)*height - j - 1)*3;
+ }
+ else
+ {
+ target_data = data + (height*(width - 1 - i) + j)*3;
+ }
+ memcpy( target_data, source_data, 3 );
+ source_data += 3;
+ }
+ }
+
+ ii = next_ii;
+ }
+
+ const unsigned char *source_alpha = M_IMGDATA->m_alpha;
+
+ if ( source_alpha )
+ {
+ unsigned char *alpha_data = image.GetAlpha();
+ unsigned char *target_alpha = 0 ;
+
+ for (long ii = 0; ii < width; )
+ {
+ long next_ii = wxMin(ii + 64, width);
+
+ for (long j = 0; j < height; j++)
+ {
+ source_alpha = M_IMGDATA->m_alpha + j*width + ii;
+
+ for (long i = ii; i < next_ii; i++)
+ {
+ if ( clockwise )
+ {
+ target_alpha = alpha_data + (i+1)*height - j - 1;
+ }
+ else
+ {
+ target_alpha = alpha_data + height*(width - i - 1) + j;
+ }
+
+ *target_alpha = *source_alpha++;
+ }
+ }
+
+ ii = next_ii;
+ }
+ }
+
+ return image;
+}
+
+wxImage wxImage::Rotate180() const
+{
+ wxImage image(MakeEmptyClone());
+
+ wxCHECK( image.IsOk(), image );
+
+ long height = M_IMGDATA->m_height;
+ long width = M_IMGDATA->m_width;
+
+ if ( HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_X) )
+ {
+ image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_X,
+ width - 1 - GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_X));
+ }
+
+ if ( HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y) )
+ {
+ image.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y,
+ height - 1 - GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_Y));
+ }
+
+ unsigned char *data = image.GetData();
+ unsigned char *alpha = image.GetAlpha();
+ const unsigned char *source_data = M_IMGDATA->m_data;
+ unsigned char *target_data = data + width * height * 3;
+
+ for (long j = 0; j < height; j++)
+ {
+ for (long i = 0; i < width; i++)
+ {
+ target_data -= 3;
+ memcpy( target_data, source_data, 3 );
+ source_data += 3;
+ }
+ }
+
+ if ( alpha )
+ {
+ const unsigned char *src_alpha = M_IMGDATA->m_alpha;
+ unsigned char *dest_alpha = alpha + width * height;
+
+ for (long j = 0; j < height; ++j)
+ {
+ for (long i = 0; i < width; ++i)
+ {
+ *(--dest_alpha) = *(src_alpha++);
+ }
+ }
+ }
+
+ return image;
+}
+
+wxImage wxImage::Mirror( bool horizontally ) const
+{
+ wxImage image(MakeEmptyClone());
+
+ wxCHECK( image.IsOk(), image );
+
+ long height = M_IMGDATA->m_height;
+ long width = M_IMGDATA->m_width;
+
+ unsigned char *data = image.GetData();
+ unsigned char *alpha = image.GetAlpha();
+ const unsigned char *source_data = M_IMGDATA->m_data;
+ unsigned char *target_data;
+
+ if (horizontally)
+ {
+ for (long j = 0; j < height; j++)
+ {
+ data += width*3;
+ target_data = data-3;
+ for (long i = 0; i < width; i++)
+ {
+ memcpy( target_data, source_data, 3 );
+ source_data += 3;
+ target_data -= 3;
+ }
+ }
+
+ if (alpha != NULL)
+ {
+ // src_alpha starts at the first pixel and increases by 1 after each step
+ // (a step here is the copy of the alpha value of one pixel)
+ const unsigned char *src_alpha = M_IMGDATA->m_alpha;
+ // dest_alpha starts just beyond the first line, decreases before each step,
+ // and after each line is finished, increases by 2 widths (skipping the line
+ // just copied and the line that will be copied next)
+ unsigned char *dest_alpha = alpha + width;
+
+ for (long jj = 0; jj < height; ++jj)
+ {
+ for (long i = 0; i < width; ++i) {
+ *(--dest_alpha) = *(src_alpha++); // copy one pixel
+ }
+ dest_alpha += 2 * width; // advance beyond the end of the next line
+ }
+ }
+ }
+ else
+ {
+ for (long i = 0; i < height; i++)
+ {
+ target_data = data + 3*width*(height-1-i);
+ memcpy( target_data, source_data, (size_t)3*width );
+ source_data += 3*width;
+ }
+
+ if ( alpha )
+ {
+ // src_alpha starts at the first pixel and increases by 1 width after each step
+ // (a step here is the copy of the alpha channel of an entire line)
+ const unsigned char *src_alpha = M_IMGDATA->m_alpha;
+ // dest_alpha starts just beyond the last line (beyond the whole image)
+ // and decreases by 1 width before each step
+ unsigned char *dest_alpha = alpha + width * height;
+
+ for (long jj = 0; jj < height; ++jj)
+ {
+ dest_alpha -= width;
+ memcpy( dest_alpha, src_alpha, (size_t)width );
+ src_alpha += width;
+ }
+ }
+ }
+
+ return image;
+}
+
+wxImage wxImage::GetSubImage( const wxRect &rect ) const
+{
+ wxImage image;
+
+ wxCHECK_MSG( IsOk(), image, wxT("invalid image") );
+
+ wxCHECK_MSG( (rect.GetLeft()>=0) && (rect.GetTop()>=0) &&
+ (rect.GetRight()<=GetWidth()) && (rect.GetBottom()<=GetHeight()),
+ image, wxT("invalid subimage size") );
+
+ const int subwidth = rect.GetWidth();
+ const int subheight = rect.GetHeight();
+
+ image.Create( subwidth, subheight, false );
+
+ const unsigned char *src_data = GetData();
+ const unsigned char *src_alpha = M_IMGDATA->m_alpha;
+ unsigned char *subdata = image.GetData();
+ unsigned char *subalpha = NULL;
+
+ wxCHECK_MSG( subdata, image, wxT("unable to create image") );
+
+ if ( src_alpha ) {
+ image.SetAlpha();
+ subalpha = image.GetAlpha();
+ wxCHECK_MSG( subalpha, image, wxT("unable to create alpha channel"));
+ }
+
+ if (M_IMGDATA->m_hasMask)
+ image.SetMaskColour( M_IMGDATA->m_maskRed, M_IMGDATA->m_maskGreen, M_IMGDATA->m_maskBlue );
+
+ const int width = GetWidth();
+ const int pixsoff = rect.GetLeft() + width * rect.GetTop();
+
+ src_data += 3 * pixsoff;
+ src_alpha += pixsoff; // won't be used if was NULL, so this is ok
+
+ for (long j = 0; j < subheight; ++j)
+ {
+ memcpy( subdata, src_data, 3 * subwidth );
+ subdata += 3 * subwidth;
+ src_data += 3 * width;
+ if (subalpha != NULL) {
+ memcpy( subalpha, src_alpha, subwidth );
+ subalpha += subwidth;
+ src_alpha += width;
+ }
+ }
+
+ return image;
+}
+
+wxImage wxImage::Size( const wxSize& size, const wxPoint& pos,
+ int r_, int g_, int b_ ) const
+{
+ wxImage image;
+
+ wxCHECK_MSG( IsOk(), image, wxT("invalid image") );
+ wxCHECK_MSG( (size.GetWidth() > 0) && (size.GetHeight() > 0), image, wxT("invalid size") );
+
+ int width = GetWidth(), height = GetHeight();
+ image.Create(size.GetWidth(), size.GetHeight(), false);
+
+ unsigned char r = (unsigned char)r_;
+ unsigned char g = (unsigned char)g_;
+ unsigned char b = (unsigned char)b_;
+ if ((r_ == -1) && (g_ == -1) && (b_ == -1))
+ {
+ GetOrFindMaskColour( &r, &g, &b );
+ image.SetMaskColour(r, g, b);
+ }
+
+ image.SetRGB(wxRect(), r, g, b);
+
+ // we have two coordinate systems:
+ // source: starting at 0,0 of source image
+ // destination starting at 0,0 of destination image
+ // Documentation says:
+ // "The image is pasted into a new image [...] at the position pos relative
+ // to the upper left of the new image." this means the transition rule is:
+ // "dest coord" = "source coord" + pos;
+
+ // calculate the intersection using source coordinates:
+ wxRect srcRect(0, 0, width, height);
+ wxRect dstRect(-pos, size);
+
+ srcRect.Intersect(dstRect);
+
+ if (!srcRect.IsEmpty())
+ {
+ // insertion point is needed in destination coordinates.
+ // NB: it is not always "pos"!
+ wxPoint ptInsert = srcRect.GetTopLeft() + pos;
+
+ if ((srcRect.GetWidth() == width) && (srcRect.GetHeight() == height))
+ image.Paste(*this, ptInsert.x, ptInsert.y);
+ else
+ image.Paste(GetSubImage(srcRect), ptInsert.x, ptInsert.y);
+ }
+
+ return image;
+}
+
+void wxImage::Paste( const wxImage &image, int x, int y )
+{
+ wxCHECK_RET( IsOk(), wxT("invalid image") );
+ wxCHECK_RET( image.IsOk(), wxT("invalid image") );
+
+ AllocExclusive();
+
+ int xx = 0;
+ int yy = 0;
+ int width = image.GetWidth();
+ int height = image.GetHeight();
+
+ if (x < 0)
+ {
+ xx = -x;
+ width += x;
+ }
+ if (y < 0)
+ {
+ yy = -y;
+ height += y;
+ }
+
+ if ((x+xx)+width > M_IMGDATA->m_width)
+ width = M_IMGDATA->m_width - (x+xx);
+ if ((y+yy)+height > M_IMGDATA->m_height)
+ height = M_IMGDATA->m_height - (y+yy);
+
+ if (width < 1) return;
+ if (height < 1) return;
+
+ // If we can, copy the data using memcpy() as this is the fastest way. But
+ // for this the image being pasted must have "compatible" mask with this
+ // one meaning that either it must not have one at all or it must use the
+ // same masked colour.
+ if ( !image.HasMask() ||
+ ((HasMask() &&
+ (GetMaskRed()==image.GetMaskRed()) &&