void Font::CalculateAdvances(ImageBuffer &image) { // Get the format and size of the surface. int width = image.Width() / GLYPHS; height = image.Height(); unsigned mask = 0xFF000000; unsigned half = 0xC0000000; int pitch = image.Width(); // advance[previous * GLYPHS + next] is the x advance for each glyph pair. // There is no advance if the previous value is 0, i.e. we are at the very // start of a string. memset(advance, 0, GLYPHS * sizeof(advance[0])); for(int previous = 1; previous < GLYPHS; ++previous) for(int next = 0; next < GLYPHS; ++next) { int maxD = 0; int glyphWidth = 0; uint32_t *begin = image.Pixels(); for(int y = 0; y < height; ++y) { // Find the last non-empty pixel in the previous glyph. uint32_t *pend = begin + previous * width; uint32_t *pit = pend + width; while(pit != pend && (*--pit & mask) < half) {} int distance = (pit - pend) + 1; glyphWidth = max(distance, glyphWidth); // Special case: if "next" is zero (i.e. end of line of text), // calculate the full width of this character. Otherwise: if(next) { // Find the first non-empty pixel in this glyph. uint32_t *nit = begin + next * width; uint32_t *nend = nit + width; while(nit != nend && (*nit++ & mask) < half) {} // How far apart do you want these glyphs drawn? If drawn at // an advance of "width", there would be: // pend + width - pit <- pixels after the previous glyph. // nit - (nend - width) <- pixels before the next glyph. // So for zero kerning distance, you would want: distance += 1 - (nit - (nend - width)); } maxD = max(maxD, distance); // Update the pointer to point to the beginning of the next row. begin += pitch; } // This is a fudge factor to avoid over-kerning, especially for the // underscore and for glyph combinations like AV. advance[previous * GLYPHS + next] = max(maxD, glyphWidth - 4) / 2; } // Set the space size based on the character width. width /= 2; height /= 2; space = (width + 3) / 6 + 1; }
void FiltersVector::Gauss(ImageBuffer& Source, ImageBuffer& Dest, int Width) { CheckCompatibility(Source, Dest); if (Width == 3) { Kernel(gaussian3, Source, Dest, Source.Step(), Dest.Step(), Source.Height()); return; } if (Width == 5) { Kernel(gaussian5, Source, Dest, Source.Step(), Dest.Step(), Source.Height()); return; } throw cl::Error(CL_INVALID_ARG_VALUE, "Invalid width used in Gauss - allowed : 3, 5"); }
void FiltersVector::Smooth(ImageBuffer& Source, ImageBuffer& Dest, int Width) { CheckCompatibility(Source, Dest); if (Width < 3 || (Width & 1) == 0) throw cl::Error(CL_INVALID_ARG_VALUE, "Invalid width used in Smooth"); Kernel(smooth, In(Source), Out(Dest), Source.Step(), Dest.Step(), Source.Height(), Width); }
void FiltersVector::Sharpen(ImageBuffer& Source, ImageBuffer& Dest, int Width) { CheckCompatibility(Source, Dest); if (Width != 3) throw cl::Error(CL_INVALID_ARG_VALUE, "Invalid width used in Sharpen - allowed : 3"); Kernel(sharpen3, In(Source), Out(Dest), Source.Step(), Dest.Step(), Source.Height()); }
void Font::Load(const string &imagePath) { // Load the texture. ImageBuffer image; if(!image.Read(imagePath)) return; LoadTexture(image); CalculateAdvances(image); SetUpShader(image.Width() / GLYPHS, image.Height()); }
void FiltersVector::Scharr(ImageBuffer& Source, ImageBuffer& Dest, int Width) { CheckCompatibility(Source, Dest); if (Width == 3) { Kernel(scharr3, Source, Dest, Source.Step(), Dest.Step(), Source.Height()); return; } throw cl::Error(CL_INVALID_ARG_VALUE, "Invalid width used in Scharr - allowed : 3"); }
void Font::LoadTexture(ImageBuffer &image) { glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, image.Width(), image.Height(), 0, GL_BGRA, GL_UNSIGNED_BYTE, image.Pixels()); }
void Font::Load(const string &imagePath) { // Load the texture. ImageBuffer *image = ImageBuffer::Read(imagePath); if(!image) return; LoadTexture(image); CalculateAdvances(image); SetUpShader(image->Width() / GLYPHS, image->Height()); delete image; }
void FiltersVector::Median(ImageBuffer& Source, ImageBuffer& Dest, int Width) { CheckCompatibility(Source, Dest); Source.SendIfNeeded(); if (Width != 3 && Width != 5) throw cl::Error(CL_INVALID_ARG_VALUE, "Invalid width used in Median - allowed : 3 or 5"); if (Width == 3) { /*if (RangeFit(Source, 16, 16)) // The cached version is slower on my GTX 680 { Kernel_(*m_CL, SelectProgram(Source), median3_cached, cl::NDRange(16, 16, 1), Source, Dest, Source.Step(), Dest.Step(), Source.Height()); return; }*/ Kernel(median3, Source, Dest, Source.Step(), Dest.Step(), Source.Height()); return; } Kernel(median5, Source, Dest, Source.Step(), Dest.Step(), Source.Height()); }
void FiltersVector::GaussianBlur(ImageBuffer& Source, ImageBuffer& Dest, float Sigma) { CheckCompatibility(Source, Dest); // Prepare mask int MaskSize = int(ceil(3 * Sigma)); if (Sigma <= 0 || MaskSize > 31) throw cl::Error(CL_INVALID_ARG_VALUE, "Invalid sigma used with GaussianBlur - allowed : 0.01-10"); uint NbElements = (MaskSize * 2 + 1 ) * (MaskSize * 2 + 1 ); std::vector<float> Mask(NbElements); GenerateBlurMask(Mask, Sigma, MaskSize); // NOTE : Maybe we should generate the mask in the device to prevent having to send that buffer // Send mask to device ReadBuffer MaskBuffer(*m_CL, Mask.data(), NbElements); // Execute kernel Kernel(gaussian_blur, In(Source), Out(Dest), Source.Step(), Dest.Step(), Source.Height(), MaskBuffer, MaskSize); }