The function below contains nested for loops. There are 3 of them. I have given the whole function below for easy understanding. I want to parallelize the code in the innermost for loop as it takes maximum CPU time. Then i can think about outer 2 for loops. I can see dependencies and internal inline functions in the innermost for loop . Can the innermost for loop be rewritten to enable parallelization using openmp pragmas. Please tell how. I am writing just the loop which i am interested in first and then the full function where this loop exists for referance.
Interested in parallelizing the loop mentioned below.
//* LOOP WHICH I WANT TO PARALLELIZE *//
for (y = 0; y < 4; y++)
{
refptr = PelYline_11 (ref_pic, abs_y++, abs_x, img_height, img_width);
LineSadBlk0 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk0 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk0 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk0 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk1 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk1 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk1 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk1 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk2 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk2 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk2 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk2 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk3 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk3 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk3 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk3 += byte_abs [*refptr++ - *orgptr++];
}
The full function where this loop exists is below for referance.
/*!
***********************************************************************
* \brief
* Setup the fast search for an macroblock
***********************************************************************
*/
void SetupFastFullPelSearch (short ref, int list) // <-- reference frame parameter, list0 or 1
{
short pmv[2];
pel_t orig_blocks[256], *orgptr=orig_blocks, *refptr, *tem; // created pointer tem
int offset_x, offset_y, x, y, range_partly_outside, ref_x, ref_y, pos, abs_x, abs_y, bindex, blky;
int LineSadBlk0, LineSadBlk1, LineSadBlk2, LineSadBlk3;
int max_width, max_height;
int img_width, img_height;
StorablePicture *ref_picture;
pel_t *ref_pic;
int** block_sad = BlockSAD[list][ref][7];
int search_range = max_search_range[list][ref];
int max_pos = (2*search_range+1) * (2*search_range+1);
int list_offset = ((img->MbaffFrameFlag)&&(img->mb_data[img->current_mb_nr].mb_field))? img->current_mb_nr%2 ? 4 : 2 : 0;
int apply_weights = ( (active_pps->weighted_pred_flag && (img->type == P_SLICE || img->type == SP_SLICE)) ||
(active_pps->weighted_bipred_idc && (img->type == B_SLICE)));
ref_picture = listX[list+list_offset][ref];
//===== Use weighted Reference for ME ====
if (apply_weights && input->UseWeightedReferenceME)
ref_pic = ref_picture->imgY_11_w;
else
ref_pic = ref_picture->imgY_11;
max_width = ref_picture->size_x - 17;
max_height = ref_picture->size_y - 17;
img_width = ref_picture->size_x;
img_height = ref_picture->size_y;
//===== get search center: predictor of 16x16 block =====
SetMotionVectorPredictor (pmv, enc_picture->ref_idx, enc_picture->mv, ref, list, 0, 0, 16, 16);
search_center_x[list][ref] = pmv[0] / 4;
search_center_y[list][ref] = pmv[1] / 4;
if (!input->rdopt)
{
//--- correct center so that (0,0) vector is inside ---
search_center_x[list][ref] = max(-search_range, min(search_range, search_center_x[list][ref]));
search_center_y[list][ref] = max(-search_range, min(search_range, search_center_y[list][ref]));
}
search_center_x[list][ref] += img->opix_x;
search_center_y[list][ref] += img->opix_y;
offset_x = search_center_x[list][ref];
offset_y = search_center_y[list][ref];
//===== copy original block for fast access =====
for (y = img->opix_y; y < img->opix_y+16; y++)
for (x = img->opix_x; x < img->opix_x+16; x++)
*orgptr++ = imgY_org [y][x];
//===== check if whole search range is inside image =====
if (offset_x >= search_range && offset_x <= max_width - search_range &&
offset_y >= search_range && offset_y <= max_height - search_range )
{
range_partly_outside = 0; PelYline_11 = FastLine16Y_11;
}
else
{
range_partly_outside = 1;
}
//===== determine position of (0,0)-vector =====
if (!input->rdopt)
{
ref_x = img->opix_x - offset_x;
ref_y = img->opix_y - offset_y;
for (pos = 0; pos < max_pos; pos++)
{
if (ref_x == spiral_search_x[pos] &&
ref_y == spiral_search_y[pos])
{
pos_00[list][ref] = pos;
break;
}
}
}
//===== loop over search range (spiral search): get blockwise SAD =====
**// =====THIS IS THE PART WHERE NESTED FOR STARTS=====**
for (pos = 0; pos < max_pos; pos++) // OUTERMOST FOR LOOP
{
abs_y = offset_y + spiral_search_y[pos];
abs_x = offset_x + spiral_search_x[pos];
if (range_partly_outside)
{
if (abs_y >= 0 && abs_y <= max_height && abs_x >= 0 && abs_x <= max_width )
{
PelYline_11 = FastLine16Y_11;
}
else
{
PelYline_11 = UMVLine16Y_11;
}
}
orgptr = orig_blocks;
bindex = 0;
for (blky = 0; blky < 4; blky++) // SECOND FOR LOOP
{
LineSadBlk0 = LineSadBlk1 = LineSadBlk2 = LineSadBlk3 = 0;
for (y = 0; y < 4; y++) //INNERMOST FOR LOOP WHICH I WANT TO PARALLELIZE
{
refptr = PelYline_11 (ref_pic, abs_y++, abs_x, img_height, img_width);
LineSadBlk0 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk0 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk0 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk0 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk1 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk1 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk1 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk1 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk2 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk2 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk2 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk2 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk3 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk3 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk3 += byte_abs [*refptr++ - *orgptr++];
LineSadBlk3 += byte_abs [*refptr++ - *orgptr++];
}
block_sad[bindex++][pos] = LineSadBlk0;
block_sad[bindex++][pos] = LineSadBlk1;
block_sad[bindex++][pos] = LineSadBlk2;
block_sad[bindex++][pos] = LineSadBlk3;
}
}
//===== combine SAD's for larger block types =====
SetupLargerBlocks (list, ref, max_pos);
//===== set flag marking that search setup have been done =====
search_setup_done[list][ref] = 1;
}
#endif // _FAST_FULL_ME_
I have rewitten the code to try to resolve dependencies in the innermost for loop ie the for(y=0;y<4;y++) and lot of LineSadBlk's. Please comment if it is wrong. I think the refptr and orgptr is sorted out by this and dependencies are resolved but LineSadBlk0,1,2,3 are still having dependencies as if we run the first and second iteration in parallel which value of LineSadBlk0,1,2,3 will be taken by the thread. How to resolve this please.
/*!
***********************************************************************
* \brief
* Setup the fast search for an macroblock
***********************************************************************
*/
void SetupFastFullPelSearch (short ref, int list) // <-- reference frame parameter, list0 or 1
{
short pmv[2];
pel_t orig_blocks[256];
//pel_t *orgptr, *refptr[4];
pel_t *orgptr[4],*refptr[4]; //defined by me new
int offset_x, offset_y, x, y, range_partly_outside, ref_x, ref_y, pos, abs_x, abs_y, bindex, blky;
int LineSadBlk0, LineSadBlk1, LineSadBlk2, LineSadBlk3;
int max_width, max_height;
int img_width, img_height;
StorablePicture *ref_picture;
pel_t *ref_pic;
int** block_sad = BlockSAD[list][ref][7];
int search_range = max_search_range[list][ref];
int max_pos = (2*search_range+1) * (2*search_range+1);
int list_offset = ((img->MbaffFrameFlag)&&(img->mb_data[img->current_mb_nr].mb_field))? img->current_mb_nr%2 ? 4 : 2 : 0;
int apply_weights = ( (active_pps->weighted_pred_flag && (img->type == P_SLICE || img->type == SP_SLICE)) ||
(active_pps->weighted_bipred_idc && (img->type == B_SLICE)));
ref_picture = listX[list+list_offset][ref];
//===== Use weighted Reference for ME ====
if (apply_weights && input->UseWeightedReferenceME)
ref_pic = ref_picture->imgY_11_w;
else
ref_pic = ref_picture->imgY_11;
max_width = ref_picture->size_x - 17;
max_height = ref_picture->size_y - 17;
img_width = ref_picture->size_x;
img_height = ref_picture->size_y;
//===== get search center: predictor of 16x16 block =====
SetMotionVectorPredictor (pmv, enc_picture->ref_idx, enc_picture->mv, ref, list, 0, 0, 16, 16); //call 1
search_center_x[list][ref] = pmv[0] / 4;
search_center_y[list][ref] = pmv[1] / 4;
if (!input->rdopt)
{
//--- correct center so that (0,0) vector is inside ---
search_center_x[list][ref] = max(-search_range, min(search_range, search_center_x[list][ref]));
search_center_y[list][ref] = max(-search_range, min(search_range, search_center_y[list][ref]));
}
search_center_x[list][ref] += img->opix_x;
search_center_y[list][ref] += img->opix_y;
offset_x = search_center_x[list][ref];
offset_y = search_center_y[list][ref];
// orgptr=orig_blocks;
orgptr[0]= orig_blocks //all org pointers defined orig blocks
orgptr[1]= orig_blocks;
orgptr[2]= orig_blocks;
orgptr[3]= orig_blocks;
//===== copy original block for fast access =====
for (y = img->opix_y; y < img->opix_y+16; y++)
for (x = img->opix_x; x < img->opix_x+16; x++)
{
//*orgptr++ = imgY_org [y][x];
*(orgptr[0])++ = imgY_org [y][x]; // img stored in all orgptr
*(orgptr[1])++ = imgY_org [y][x];
*(orgptr[2])++ = imgY_org [y][x];
*(orgptr[3])++ = imgY_org [y][x];
}
//===== check if whole search range is inside image =====
if (offset_x >= search_range && offset_x <= max_width - search_range &&
offset_y >= search_range && offset_y <= max_height - search_range )
{
range_partly_outside = 0; PelYline_11 = FastLine16Y_11; //search range is fully inside image
}
else
{
range_partly_outside //search range is partly outside image
}
//===== determine position of (0,0)-vector =====
if (!input->rdopt)
{
ref_x = img->opix_x - offset_x;
ref_y = img->opix_y - offset_y;
for (pos = 0; pos < max_pos; pos++)
{
if (ref_x == spiral_search_x[pos] &&
ref_y == spiral_search_y[pos])
{
pos_00[list][ref] = pos;
break;
}
}
}
//===== loop over search range (spiral search): get blockwise SAD =====
for (pos = 0; pos < max_pos; pos++)
{
abs_y = offset_y + spiral_search_y[pos];
abs_x = offset_x + spiral_search_x[pos];
if (range_partly_outside)
{
if (abs_y >= 0 && abs_y <= max_height &&
abs_x >= 0 && abs_x <= max_width )
{
PelYline_11 = FastLine16Y_11; //call 2
}
else
{
PelYline_11 = UMVLine16Y_11; //call 3
}
}
//orgptr=orig_blocks;
orgptr[0]=orig_blocks;
orgptr[1]=orgptr[0]+16;
orgptr[2]=orgptr[1]+16;
orgptr[3]=orgptr[2]+16;
bindex = 0;
for (blky = 0; blky < 4; blky++)
{
LineSadBlk0 = LineSadBlk1 = LineSadBlk2 = LineSadBlk3 = 0;
// i added the following to take refptr out of loop
refptr[0] = PelYline_11 (ref_pic, abs_y, abs_x, img_height, img_width); //call either 2 or 3
abs_y++;
refptr[1] = PelYline_11 (ref_pic, abs_y, abs_x, img_height, img_width); //call either 2 or 3
abs_y++;
refptr[2] = PelYline_11 (ref_pic, abs_y, abs_x, img_height, img_width); //call either 2 or 3
abs_y++;
refptr[3] = PelYline_11 (ref_pic, abs_y, abs_x, img_height, img_width); //call either 2 or 3
abs_y++;
omp_set_num_threads(4);
#pragma omp parallel for reduction(+:LineSadBlk0,LineSadBlk1,LineSadBlk2,LineSadBlk3)
for (y = 0; y < 4; y++)
{
{
LineSadBlk0 += byte_abs [*(refptr[y])++ - *(orgptr[y])++];
LineSadBlk0 += byte_abs [*(refptr[y])++ - *(orgptr[y])++];
LineSadBlk0 += byte_abs [*(refptr[y])++ - *(orgptr[y])++];
LineSadBlk0 += byte_abs [*(refptr[y])++ - *(orgptr[y])++];
}
{
LineSadBlk1 += byte_abs [*(refptr[y])++ - *(orgptr[y])++];
LineSadBlk1 += byte_abs [*(refptr[y])++ - *(orgptr[y])++];
LineSadBlk1 += byte_abs [*(refptr[y])++ - *(orgptr[y])++];
LineSadBlk1 += byte_abs [*(refptr[y])++ - *(orgptr[y])++];
}
{
LineSadBlk2 += byte_abs [*(refptr[y])++ - *(orgptr[y])++];
LineSadBlk2 += byte_abs [*(refptr[y])++ - *(orgptr[y])++];
LineSadBlk2 += byte_abs [*(refptr[y])++ - *(orgptr[y])++];
LineSadBlk2 += byte_abs [*(refptr[y])++ - *(orgptr[y])++];
}
{
LineSadBlk3 += byte_abs [*(refptr[y])++ - *(orgptr[y])++];
LineSadBlk3 += byte_abs [*(refptr[y])++ - *(orgptr[y])++];
LineSadBlk3 += byte_abs [*(refptr[y])++ - *(orgptr[y])++];
LineSadBlk3 += byte_abs [*(refptr[y])++ - *(orgptr[y])++];
}
}
}
block_sad[bindex++][pos] = LineSadBlk0;
block_sad[bindex++][pos] = LineSadBlk1;
block_sad[bindex++][pos] = LineSadBlk2;
block_sad[bindex++][pos] = LineSadBlk3;
}
}
//===== combine SAD's for larger block types =====
SetupLargerBlocks (list, ref, max_pos); //call4
//===== set flag marking that search setup have been done =====
search_setup_done[list][ref] = 1;
}
#endif // _FAST_FULL_ME_
Related
I would like to check if an object is in range in a matrix.
A 1 range would be 9 blocks around the player (orange).
But a two range would be 25 blocks (blue). The player is the red cross.I tried the following code:`
int size = ((range * 2) +1) * ((range * 2) + 1);
int sq = (range * 2) + 1;
int startX = x - range; if (startX < 0) startX = 0;
int startY = y - range; if (startY < 0) startY = 0;
int endX = x + range; if (endX > arrayWitdth) endX = arrayWitdth;
int endY = y + range; if (endY > arrayLenght) endY = arrayLenght;
//printf("Range: %d\n", range);
for (size_t i = startX; i < endX; i++)
{
for (size_t j = startY; j < endY; j++)
{
//printf("Looking at (%d,%d)\n", i, j);
if (map[i][j] == charTocheck) return 1;
}
}
`
You don't check the last block, so the correct implementation would be:
int size = ((range * 2) +1) * ((range * 2) + 1);
int sq = (range * 2) + 1;
int startX = x - range; if (startX < 0) startX = 0;
int startY = y - range; if (startY < 0) startY = 0;
int endX = x + range + 1; if (endX > arrayWitdth) endX = arrayWitdth;
int endY = y + range + 1; if (endY > arrayLenght) endY = arrayLenght;
//printf("Range: %d\n", range);
for (size_t i = startX; i < endX; i++)
{
for (size_t j = startY; j < endY; j++)
{
//printf("Looking at (%d,%d)\n", i, j);
if (map[i][j] == charTocheck) return 1;
}
}
notice that endX and endY have slightly changed.
In a rectangle with given height and width. I'm supposed to find the square with most 1s and print the number of 1s on stdout, also in that same square there must not be more 2s than half of 1s, i.e:((# of 1s) /2) >= (# of 2s).
Square is always at least 2x2 big.
So for the input (first two numbers are height and width):
6 8
0 0 2 2 2 1 2 1
0 1 2 2 1 0 1 1
0 0 1 0 1 2 0 2
2 1 0 2 2 1 1 1
1 2 1 0 0 0 1 0
1 2 0 1 1 2 1 1
The correct answer is 9.(square is 5x5 big and the upperleft corner is on second row, third column)
Now i managed to somewhat write a program that does this correctly, but it's too slow.
So my I'm asking for an advice how to write the algorithm so that it solves this: https://justpaste.it/1cfem under 1 second(correct answer 15) and this: https://justpaste.it/1cfen under 4 seconds(correct answer 556).
EDIT: I forgot to mention by square I mean only the perimeter of the square (the four sides)
My code works something like this:
Iterate trough all the fields in the input and iterate trough all the possible squares that start in this field(starting from the biggest square possible). Then I have some conditions like that I break the iteration when the possible perimeter of the square is smaller than the already biggest number of 1s i have found so far in a perimete etc. Also when I'm trying to find the squares starting from the given field, I remember the up side and left side of the preceding square and then just decrement it(if there is a 1 or 2).
But this isn't enough, since solution like this solves the second input in like 1 and a half minute a I need it in four seconds.
The code:
NOTE: the minerals represent 1s and toxics represent 2s
#include <stdio.h>
#include <stdlib.h>
int maxMinerals;
void traverseforH(const int const *map, const int height, const int width) {
const int h1 = height - 1;
const int w1 = width - 1;
int lineOffset = 0;
for (int startY = 0; startY < h1; startY++) {
int yside = height - startY;
if (!(yside * 2 + (yside - 2)*2 > maxMinerals)) {
break;
}
for (int startX = 0; startX < w1; startX++) {
int xside = width - startX;
if (!(xside * 2 + (xside - 2)*2 > maxMinerals)) {
break;
}
int maxBoundl = width;
int maxBoundm = width;
if (startY + maxBoundm - height - startX > 0) {
maxBoundl = height;
maxBoundm = height;
if (startX - startY > 0) {
maxBoundl = maxBoundl + startY - startX;
} else {
maxBoundm = maxBoundm + startX - startY;
}
} else if (startY - startX > 0) {
maxBoundm = maxBoundm + startY - startX;
maxBoundl = maxBoundm;
maxBoundm = maxBoundm + startX - startY;
} else {
maxBoundl = maxBoundl + startY - startX;
}
int mBw = (maxBoundl - 1) * width;
int toxicsLeftSide = 0;
int mineralsLeftSide = 0;
int toxicsUpSide = 0;
int mineralsUpSide = 0;
int mw;
int lastMinerals = 0;
int toxics = 0;
int sidey = lineOffset + width;
for (int x = startX; x < maxBoundm; x++) {
mw = x + lineOffset;
if (map[mw] == 1) {
mineralsUpSide++;
lastMinerals++;
} else if (map[mw]) {
toxicsUpSide++;
toxics++;
}
mw = x + mBw;
if (map[mw] == 1) {
lastMinerals++;
} else if (map[mw]) {
toxics++;
}
}
for (int y = startY + 1; y < maxBoundl - 1; y++) {
mw = startX + sidey;
if (map[mw] == 1) {
mineralsLeftSide++;
lastMinerals++;
} else if (map[mw]) {
toxicsLeftSide++;
toxics++;
}
mw = maxBoundm - 1 + sidey;
if (map[mw] == 1) {
lastMinerals++;
} else if (map[mw]) {
toxics++;
}
sidey = sidey + width;
}
if (map[startX + mBw] == 1) {
mineralsLeftSide++;
} else if (map[startX + mBw]) {
toxicsLeftSide++;
}
int upsideData [2];
upsideData[0] = mineralsUpSide;
upsideData[1] = toxicsUpSide;
if (!(lastMinerals / 2.0 < toxics) && lastMinerals > maxMinerals) {
maxMinerals = lastMinerals;
}
mBw = mBw - width;
int noOfSquares;
if (xside < yside) {
noOfSquares = xside - 1;
} else {
noOfSquares = yside - 1;
}
for (int k = 1; k < noOfSquares; k++) {
int maxBoundy = maxBoundl - k;
int maxBoundx = maxBoundm - k;
if (!(((maxBoundx - startX)*2 + (maxBoundx - 2 - startX)*2) > maxMinerals)) {
break;
}
sidey = lineOffset + width;
lastMinerals = 0;
toxics = 0;
if (map[maxBoundx + lineOffset] == 1) {
mineralsUpSide--;
} else if (map[maxBoundx + lineOffset]) {
toxicsUpSide--;
}
if (map[startX + mBw + width] == 1) {
mineralsLeftSide--;
} else if (map[startX + mBw + width]) {
toxicsLeftSide--;
}
for (int x = startX + 1; x < maxBoundx; x++) {
mw = x + mBw;
if (map[mw] == 1) {
lastMinerals++;
} else if (map[mw]) {
toxics++;
}
}
for (int y = startY + 1; y < maxBoundy - 1; y++) {
mw = maxBoundx - 1 + sidey;
if (map[mw] == 1) {
lastMinerals++;
} else if (map[mw]) {
toxics++;
}
sidey = sidey + width;
}
int finalMinerals = lastMinerals + mineralsLeftSide + mineralsUpSide;
int finalToxics = toxics + toxicsLeftSide + toxicsUpSide;
if (!(finalMinerals / 2.0 < finalToxics) && finalMinerals > maxMinerals) {
maxMinerals = finalMinerals;
}
mBw = mBw - width;
}
}
lineOffset = lineOffset + width;
}
printf("%d\n", maxMinerals);
}
void traverseforW(int *map, const int height, const int width) {
int h1 = height - 1;
int w1 = width - 1;
int lineOffset = 0;
for (int startY = 0; startY < h1; startY++) {
int yside = height - startY;
if (!(yside * 2 + (yside - 2)*2 > maxMinerals)) {
break;
}
for (int startX = 0; startX < w1; startX++) {
int xside = width - startX;
if (!(xside * 2 + (xside - 2)*2 > maxMinerals)) {
break;
}
int maxBoundl = height;
int maxBoundm = height;
if (startX + maxBoundl - width - startY > 0) {
maxBoundl = width;
maxBoundm = width;
if (startX - startY > 0) {
maxBoundl = maxBoundl + startY - startX;
} else {
maxBoundm = maxBoundm + startX - startY;
}
} else if (startY - startX > 0) {
maxBoundm = maxBoundm + startX - startY;
} else {
maxBoundl = maxBoundl + startX - startY;
maxBoundm = maxBoundl;
maxBoundl = maxBoundl + startY - startX;
}
int mBw = (maxBoundl - 1) * width;
int toxicsLeftSide = 0;
int mineralsLeftSide = 0;
int toxicsUpSide = 0;
int mineralsUpSide = 0;
int mw;
int lastMinerals = 0;
int toxics = 0;
int sidey = lineOffset + width;
for (int x = startX; x < maxBoundm; x++) {
mw = x + lineOffset;
if (map[mw] == 1) {
mineralsUpSide++;
lastMinerals++;
} else if (map[mw]) {
toxicsUpSide++;
toxics++;
}
mw = x + mBw;
if (map[mw] == 1) {
lastMinerals++;
} else if (map[mw]) {
toxics++;
}
}
for (int y = startY + 1; y < maxBoundl - 1; y++) {
mw = startX + sidey;
if (map[mw] == 1) {
mineralsLeftSide++;
lastMinerals++;
} else if (map[mw]) {
toxicsLeftSide++;
toxics++;
}
mw = maxBoundm - 1 + sidey;
if (map[mw] == 1) {
lastMinerals++;
} else if (map[mw]) {
toxics++;
}
sidey = sidey + width;
}
if (map[startX + mBw] == 1) {
mineralsLeftSide++;
} else if (map[startX + mBw]) {
toxicsLeftSide++;
}
if (!(lastMinerals / 2.0 < toxics) && lastMinerals > maxMinerals) {
maxMinerals = lastMinerals;
}
mBw = mBw - width;
int noOfSquares;
if (xside < yside) {
noOfSquares = xside - 1;
} else {
noOfSquares = yside - 1;
}
for (int k = 1; k < noOfSquares; k++) {
int maxBoundy = maxBoundl - k;
int maxBoundx = maxBoundm - k;
if (!(((maxBoundx - startX)*2 + (maxBoundx - 2 - startX)*2) > maxMinerals)) {
break;
}
sidey = lineOffset + width;
lastMinerals = 0;
toxics = 0;
if (map[maxBoundx + lineOffset] == 1) {
mineralsUpSide--;
} else if (map[maxBoundx + lineOffset]) {
toxicsUpSide--;
}
if (map[startX + mBw + width] == 1) {
mineralsLeftSide--;
} else if (map[startX + mBw + width]) {
toxicsLeftSide--;
}
int finalMinerals = mineralsUpSide + mineralsLeftSide;
int finalToxics = toxicsLeftSide + toxicsUpSide;
for (int x = startX + 1; x < maxBoundx; x++) {
mw = x + mBw;
if (map[mw] == 1) {
lastMinerals++;
} else if (map[mw]) {
toxics++;
}
}
for (int y = startY + 1; y < maxBoundy - 1; y++) {
mw = maxBoundx - 1 + sidey;
if (map[mw] == 1) {
lastMinerals++;
} else if (map[mw]) {
toxics++;
}
sidey = sidey + width;
}
finalMinerals += lastMinerals;
finalToxics += toxics;
if (!(finalMinerals / 2.0 < finalToxics) && finalMinerals > maxMinerals) {
maxMinerals = finalMinerals;
}
mBw = mBw - width;
}
}
lineOffset = lineOffset + width;
}
printf("%d\n", maxMinerals);
}
int main() {
char hw[14];
FILE * file = fopen("pub01.in", "r");
char c;
int k = 0;
while ((c = fgetc(file)) != '\n') {
hw[k] = c;
k++;
}
int h, w;
sscanf(hw, "%d %d", &h, &w);
int size = h * w;
int* input = malloc(size * sizeof (int) + 1);
k = 0;
while ((c = fgetc(file)) != EOF) {
if (c == '0' || c == '1' || c == '2') {
input[k] = c - '0';
k++;
}
}
input[k] = '\0';
if (h > w) {
traverseforH(input, h, w);
} else {
traverseforW(input, h, w);
}
return 0;
}
Preprocess step:
First pre-process matrix, using prefix sum method all rows and columns so that you will be able to calculate # of 1s and # of 2s in the perimeter of square in O(1).
By now you will have 4 data-structures: rowSumFor1, rowSumFor2, colSumFor1, colSumFor2. For example: rowSumFor1[i][j] would tell us # of 1s in ith row for column indices between 0 and j inclusive.
Time complexity: O(w x h)
Complete Code:
#include<stdio.h>
int min(int a,int b){
return (a<=b)?a:b;
}
int max(int a,int b){
return (a>=b)?a:b;
}
// currently hard-coding dimensions for test purposes
// horizontal sums
int rowSumFor1[600][600];
int rowSumFor2[600][600];
// vertical sums
int colSumFor1[600][600];
int colSumFor2[600][600];
int main(){
int w,h;
scanf("%d %d",&h,&w);
for(int row=1;row <= h;row++)for(int col=1;col <= w;col++){
int temp;
scanf("%d",&temp);
// first add previous sum
rowSumFor1[row][col]=rowSumFor1[row][col - 1];
rowSumFor2[row][col]=rowSumFor2[row][col - 1];
colSumFor1[col][row]=colSumFor1[col][row - 1];
colSumFor2[col][row]=colSumFor2[col][row - 1];
if(temp==1){
rowSumFor1[row][col]++;
colSumFor1[col][row]++;
}
else if(temp==2){
rowSumFor2[row][col]++;
colSumFor2[col][row]++;
}
else{
// do nothing
}
}
int result = 0,rowId,colId,mlength;
for(int len=min(w,h); len > 1 ; len-- ) // iteration on possible lengths
{
for(int row=1;row <= (h - len + 1);row++)for(int col=1;col <= (w - len + 1);col++){ // iteration on all co-ordinates as upper-left corner of our square
// Do calculation here for properties and necessary checking constraints for validity of this square
// Note: not checking trivial conditions like boundary conditions in square, you will have to!!
// Beware of over-counting of corners here, one way to avoid is to select indices such that they don't overcount corners
// 4x4 square example for counting
// aaab
// d b
// d b
// dccc
int topEdge1 = rowSumFor1[row][col + len - 2] - rowSumFor1[row][col - 1];
int bottomEdge1 = rowSumFor1[row + len - 1][col + len - 1] - rowSumFor1[row + len - 1][col];
int leftEdge1 = colSumFor1[col][row + len - 1] - colSumFor1[col][row];
int rightEdge1 = colSumFor1[col + len - 1][row + len - 2] - colSumFor1[col + len - 1][row - 1];
int ones= topEdge1 + bottomEdge1 + leftEdge1 + rightEdge1; // # of 1s on perimeter of this square
int topEdge2 = rowSumFor2[row][col + len - 2] - rowSumFor2[row][col-1];
int bottomEdge2 = rowSumFor2[row+len-1][col+len-1] - rowSumFor2[row+len-1][col];
int leftEdge2 = colSumFor2[col][row + len - 1] - colSumFor2[col][row];
int rightEdge2 = colSumFor2[col + len - 1][row + len - 2] - colSumFor2[col + len -1][row - 1];
int twos= topEdge2 + bottomEdge2 + leftEdge2 + rightEdge2; // # of 2s on perimeter of this square
if(ones >= 2* twos){
if(ones > result){
result = ones;
rowId = row;
colId = col;
mlength = len;
}
}
}
}
printf("%d %d %d\n",rowId,colId,mlength);
printf("%d\n",result);
return 0;
}
Time complexity: O(w x h x min(w,h))
EDIT:
Replaced pseudo-code with complete code. It results as expected for all 3 tests presented by OP.
I made something to simulate a rope and it glitches out when the thing being held by the rope is under the thing holding the rope. This works when not in a for loop and its just one segment but i want to be able to simulate multiple segments.
code:
//Declaring variables
int linkCount = 3;
float Rotation = 0;
float R = radians(Rotation);
float x[] = new float[linkCount];
float y[] = new float[linkCount];
float xVel[] = new float[linkCount];
float yVel[] = new float[linkCount];
float ropeLength = 50;
float velMX;
float velMY;
float spring = 1;
void setup() {
size(1280, 500, P3D);
stroke(0);
fill(0);
}
void draw() {
background(255);
// Updating velocitys
for(int i=1; i < linkCount; i++) {
x[i] = x[i] + xVel[i];
y[i] = y[i] + yVel[i];
}
// The two lines below are not needed and will most likely will be used in the futre
velMX = pmouseX - mouseX;
velMY = pmouseY - mouseY;
// Setting the start of the rope to the mouse
x[0] = mouseX;
y[0] = mouseY;
// if(mousePressed) {
calcRopes();
// }
}
void calcRopes() {
for(int i = 1; i < linkCount; i++) {
// Getting a radian that points toward the last subrope
R = atan2(-(x[i] - x[i-1]), -(y[i] - x[i-1]));
// Drawing the rope
line(x[i], y[i], x[i - 1], y[i - 1]);
// If the segment is farther than the rope length it moves it inside the rope length based on the R radian
if(dist(x[i], y[i], x[i - 1], y[i - 1]) > ropeLength) {
x[i] = x[i] + ((dist(x[i], y[i], x[i - 1], y[i - 1]) - (ropeLength)) * sin(R));
y[i] = y[i] + ((dist(x[i], y[i], x[i - 1], y[i - 1]) - (ropeLength)) * cos(R));
// xVel[i] = ((dist(x[i], y[i], x[i-1], y[i-1]) - (ropeLength + 10)) * sin(R));
// yVel[i] = ((dist(x[i], y[i], x[i-1], y[i-1]) - (ropeLength + 10)) * cos(R));
}
}
}
How can I resolve this?
First of all: Don't use variables to set your screen size, processing 2.2.1 doesn't like that and Processing 3 doesn't even allow that.
Secondly:
line(x[i], y[i], x[i--], y[i--]);
Statements like this from calcRopes() will cause ArrayIndexOutOfBoundsException. Why are you using i-- for? Everytime you use that, i is decremented once. So starting with i=2, your value for i becomes -1 after the "line(x[i], y[i], x[i--], y[i--]);"
Edit:
This worked for me but I think it needs some adjustment still.
int linkCount = 10;
float Rotation = 0;
float R = radians(Rotation);
float x[] = new float[linkCount];
float y[] = new float[linkCount];
float xVel[] = new float[linkCount];
float yVel[] = new float[linkCount];
float ropeLength = 100;
float velMX;
float velMY;
float spring = 1;
void setup() {
size(1280, 500); //CHANGED THIS***********************
}
void draw() {
stroke(0);
fill(0);
background(255);
for (int i=0; i < linkCount; i++) {
x[i] = x[i] + xVel[i];
y[i] = y[i] + yVel[i];
}
velMX = pmouseX - mouseX;
velMY = pmouseY - mouseY;
x[1] = mouseX;
y[1] = mouseY;
// if(mousePressed) {
calcRopes();
// }
}
//REPLACED ALL i-- with i-1 (just to see if it works)***********************
void calcRopes() {
for (int i = 1; i < linkCount; i++) {
if (i<x.length && i<y.length) {
R = atan2(-(x[i] - mouseX), -(y[i] - mouseY));
line(x[i], y[i], x[i-1], y[i-1]);
if (dist(x[i], y[i], x[i - 1], y[i - 1]) > ropeLength) {
xVel[i] = ((dist(x[i], y[i], x[i-1], y[i-1]) - (ropeLength - 1)) * sin(R)) / spring;
yVel[i] = ((dist(x[i], y[i], x[i-1], y[i-1]) - (ropeLength - 1)) * cos(R)) / spring;
}
}
}
}
I found out this line:
R = atan2(-(x[i] - x[i-1]), -(y[i] - x[i-1]));
Had a error which made it miscalculate the radian
R = atan2(-(x[i] - x[i-1]), -(y[i] - y[i-1]));
Is what I wanted.
Im still having issues with making it look realistic but i can solve that myself.
I am trying to make blobs finding algorithm with 8 connectivity for binary image(monochrome) (coordinates of bounding boxes up-left and down-right dots)which use small amount of memory (needed because the large resolution of the image) on C++.
There are such tools like OpenCV, but it has a lot of filters and is too slow if you want to detect each blob in binary image, there is also CvBlobsLib but the support is outdated(last version is before 5 years) and I couldn't set it up for Visual Studio 2013 (it must be compiled with Cmake, and it is giving errors). In wikipedia there are two types of algorithms - "one component of a time" and "two-pass" connected-component , but they both use labels, which mean you will have another 2D array of integers, but this will take a lot of memory because of the size of int(4 bytes), and we need int because of the image size and possibility of more than 65535 labels(which is short). If it is even short it will take twice less memory, which is again a lot of it. I found a "quickblob" written in C quicblobsalgol but I couldn't run it from the source(but exe is working properly), tried to analyze the code, and I got something, but the whole idea behind it stayed vague for me, so I tried also something like floodFill algorithm and something like "disjoined-set data structure" link which to hold the blobs, and this means the used memory theoretically is defined of the number of blobs(single black pixels are not recognize as blobs). Here is the C++ code:
#include <cstdlib>
#include <iostream>
#include <ctime>
#include <math.h>
#define ROWS 4000
#define COLS 4000
#define BLOBS 1000000
using namespace std;
void floodFillAlgorithm(short(&arr)[ROWS][COLS]);
int recurciveMarkBlob(short(&arr)[ROWS][COLS], int **ptr_labels, int i, int j, int group);
int main(){
short arr[ROWS][COLS];
srand((unsigned int)time(0)); // use current time as seed for random generator
for (int i = 0; i < ROWS; i++)
{
for (int j = 0; j < COLS; j++)
{
arr[i][j] = rand() % 2;
}
}
/*for (int i = 0; i < ROWS; i++)
{
for (int j = 0; j < COLS; j++)
{
cout << arr[i][j] << '\t';
}
cout << '\n';
}*/
floodFillAlgorithm(arr);
cout << '\n';
cout << '\n';
/*for (int i = 0; i < ROWS; i++)
{
for (int j = 0; j < COLS; j++)
{
cout << arr[i][j] << '\t';
}
cout << '\n';
}*/
system("PAUSE");
return 0;}
void floodFillAlgorithm(short(&arr)[ROWS][COLS])
{
int group = 0;
int **ptr_labels;
ptr_labels = (int **)malloc(BLOBS * sizeof(int*));
if (ptr_labels == 0)
{
printf("ERROR: Out of memory\n");
}
for (int i = 0; i < BLOBS; i++)
{
ptr_labels[i] = NULL;
}
for (int i = 0; i < ROWS; i++)
{
for (int j = 0; j < COLS; j++)
{
if (arr[i][j] == 1)
{
recurciveMarkBlob(arr, ptr_labels,i, j, ++group);
arr[i][j] = 1;
}
}
}
int count = 0;
for (int i = 0; i < BLOBS; i++)
{
if (ptr_labels[i] != NULL)
{
count++;
//cout << "Label: " << i << " ; X1: " << ptr_labels[i][0] << " ; Y1: " << ptr_labels[i][1] << " ; X2: " << ptr_labels[i][2] << " ; Y2: " << ptr_labels[i][3] << " ; X3: " << ptr_labels[i][4] << " ; Y3: " << ptr_labels[i][5] << " ; POINTS: " << ptr_labels[i][6] << endl;
}
}
cout << "Count: " << count << endl;
system("PAUSE");
for (int i = 0; i < BLOBS; i++)
{
if (ptr_labels[i] != NULL)
{
free(ptr_labels[i]);
}
}
free(ptr_labels);
}
int recurciveMarkBlob(short(&arr)[ROWS][COLS], int **ptr_labels, int i, int j, int group)
{
//cout << " i : " << i << " j: " << j << endl;
if (j != 0)
{
if ((arr[i][j] == arr[i][j - 1]) && (arr[i][j - 1] == 1))
{
if (ptr_labels[group] == NULL)
{
ptr_labels[group] = (int *)malloc(7 * sizeof(int*));
ptr_labels[group][0] = j - 1;
ptr_labels[group][1] = i;
ptr_labels[group][2] = j;
ptr_labels[group][3] = i;
ptr_labels[group][4] = j;
ptr_labels[group][5] = i;
ptr_labels[group][6] = 2; // taken points (area) for current shape
}
else
{
if (ptr_labels[group][0] > j - 1)
{
ptr_labels[group][0] = j - 1;
}
ptr_labels[group][6]++;
}
arr[i][j] = 0;
recurciveMarkBlob(arr, ptr_labels, i, j - 1, group);
arr[i][j] = 1;
}
}
if (j != COLS - 1)
{
if ((arr[i][j] == arr[i][j + 1]) && (arr[i][j + 1] == 1))
{
if (ptr_labels[group] == NULL)
{
ptr_labels[group] = (int *)malloc(7 * sizeof(int*));
ptr_labels[group][0] = j;
ptr_labels[group][1] = i;
ptr_labels[group][2] = j + 1;
ptr_labels[group][3] = i;
ptr_labels[group][4] = j;
ptr_labels[group][5] = i;
ptr_labels[group][6] = 2; // taken points (area) for current shape
}
else
{
if (ptr_labels[group][2] < j + 1)
{
ptr_labels[group][2] = j + 1;
}
ptr_labels[group][6]++;
}
arr[i][j] = 0;
recurciveMarkBlob(arr, ptr_labels, i, j + 1, group);
arr[i][j] = 1;
}
}
if (i != 0)
{
if ((arr[i][j] == arr[i - 1][j]) && (arr[i - 1][j] == 1))
{
if (ptr_labels[group] == NULL)
{
ptr_labels[group] = (int *)malloc(7 * sizeof(int*));
ptr_labels[group][0] = j;
ptr_labels[group][1] = i - 1;
ptr_labels[group][2] = j;
ptr_labels[group][3] = i;
ptr_labels[group][4] = j;
ptr_labels[group][5] = i;
ptr_labels[group][6] = 2; // taken points (area) for current shape
}
else
{
if (ptr_labels[group][1] > i - 1)
{
ptr_labels[group][1] = i - 1;
}
ptr_labels[group][6]++;
}
arr[i][j] = 0;
recurciveMarkBlob(arr, ptr_labels, i - 1, j, group);
arr[i][j] = 1;
}
}
if (i != ROWS - 1)
{
if ((arr[i][j] == arr[i + 1][j]) && (arr[i + 1][j] == 1))
{
if (ptr_labels[group] == NULL)
{
ptr_labels[group] = (int *)malloc(7 * sizeof(int*));
ptr_labels[group][0] = j;
ptr_labels[group][1] = i;
ptr_labels[group][2] = j;
ptr_labels[group][3] = i + 1;
ptr_labels[group][4] = j;
ptr_labels[group][5] = i;
ptr_labels[group][6] = 2; // taken points (area) for current shape
}
else
{
if (ptr_labels[group][3] < i + 1)
{
ptr_labels[group][3] = i + 1;
}
ptr_labels[group][6]++;
}
arr[i][j] = 0;
recurciveMarkBlob(arr, ptr_labels, i + 1, j, group);
arr[i][j] = 1;
}
}
if ((i != 0) && (j != 0))
{
if ((arr[i][j] == arr[i - 1][j - 1]) && (arr[i - 1][j - 1] == 1))
{
if (ptr_labels[group] == NULL)
{
ptr_labels[group] = (int *)malloc(7 * sizeof(int*));
ptr_labels[group][0] = j - 1;
ptr_labels[group][1] = i - 1;
ptr_labels[group][2] = j;
ptr_labels[group][3] = i;
ptr_labels[group][4] = j;
ptr_labels[group][5] = i;
ptr_labels[group][6] = 2; // taken points (area) for current shape
}
else
{
if (ptr_labels[group][0] > j - 1)
{
ptr_labels[group][0] = j - 1;
}
if (ptr_labels[group][1] > i - 1)
{
ptr_labels[group][1] = i - 1;
}
ptr_labels[group][6]++;
}
arr[i][j] = 0;
recurciveMarkBlob(arr, ptr_labels, i - 1, j - 1, group);
arr[i][j] = 1;
}
}
if ((i != 0) && (j != COLS - 1))
{
//cout << "i: " << i << " ; j: " << j << endl;
if ((arr[i][j] == arr[i - 1][j + 1]) && (arr[i - 1][j + 1] == 1))
{
//cout << "i: " << i << " ; j: " << j << endl;
if (ptr_labels[group] == NULL)
{
ptr_labels[group] = (int *)malloc(7 * sizeof(int*));
ptr_labels[group][0] = j;
ptr_labels[group][1] = i - 1;
ptr_labels[group][2] = j + 1;
ptr_labels[group][3] = i;
ptr_labels[group][4] = j;
ptr_labels[group][5] = i;
ptr_labels[group][6] = 2; // taken points (area) for current shape
//cout << "Label: " << group << " ; X1: " << ptr_labels[group][0] << " ; Y1: " << ptr_labels[group][1] << " ; X2: " << ptr_labels[group][2] << " ; Y2: " << ptr_labels[group][3] << endl;
}
else
{
if (ptr_labels[group][2] < j + 1)
{
ptr_labels[group][2] = j + 1;
}
if (ptr_labels[group][1] > i - 1)
{
ptr_labels[group][1] = i - 1;
}
ptr_labels[group][6]++;
}
arr[i][j] = 0;
recurciveMarkBlob(arr, ptr_labels, i - 1, j + 1, group);
arr[i][j] = 1;
}
}
if ((i != ROWS - 1) && (j != 0))
{
if ((arr[i][j] == arr[i + 1][j - 1]) && (arr[i + 1][j - 1] == 1))
{
if (ptr_labels[group] == NULL)
{
ptr_labels[group] = (int *)malloc(7 * sizeof(int*));
ptr_labels[group][0] = j - 1;
ptr_labels[group][1] = i;
ptr_labels[group][2] = j;
ptr_labels[group][3] = i + 1;
ptr_labels[group][4] = j;
ptr_labels[group][5] = i;
ptr_labels[group][6] = 2; // taken points (area) for current shape
}
else
{
if (ptr_labels[group][0] > j - 1)
{
ptr_labels[group][0] = j - 1;
}
if (ptr_labels[group][3] < i + 1)
{
ptr_labels[group][3] = i + 1;
}
ptr_labels[group][6]++;
}
arr[i][j] = 0;
recurciveMarkBlob(arr, ptr_labels, i + 1, j - 1, group);
arr[i][j] = 1;
}
}
if ((i != ROWS - 1) && (j != COLS - 1))
{
if ((arr[i][j] == arr[i + 1][j + 1]) && (arr[i + 1][j + 1] == 1))
{
if (ptr_labels[group] == NULL)
{
ptr_labels[group] = (int *)malloc(7 * sizeof(int*));
ptr_labels[group][0] = j;
ptr_labels[group][1] = i;
ptr_labels[group][2] = j + 1;
ptr_labels[group][3] = i + 1;
ptr_labels[group][4] = j; // x of pixel in black
ptr_labels[group][5] = i; // y of pixel in black
ptr_labels[group][6] = 2; // taken points (area) for current shape
}
else
{
if (ptr_labels[group][2] < j + 1)
{
ptr_labels[group][2] = j + 1;
}
if (ptr_labels[group][3] < i + 1)
{
ptr_labels[group][3] = i + 1;
}
ptr_labels[group][6]++;
}
arr[i][j] = 0;
recurciveMarkBlob(arr, ptr_labels, i + 1, j + 1, group);
arr[i][j] = 1;
}
}
/**/
arr[i][j] = 0;
return 0;
}
The main question is why before end of the main function so much RAM is still in use(147 MB). The tail recursion "recurciveMarkBlob()" is using parameters by value i,j, group, and dynamic allocation of memory and that is why the memory temporary jumps to 600 MB(mostly from the parameters), after freeing the dynamically allocated memory it still takes 148 MB, the image is 4 000 x 4 000 x 2 bytes = 16 000 000 bytes = 16 MB. I have read about "function taken memory" here but I still cant understand why. If someone can explain it with assembler code what is happening and is this occurrence normal. I am using Release mode release vs debug
system("PAUSE") in main()
In process of recursion
Also everyone can give idea for fast and low memory taking algorithm for blob detection of large binary images.
The elementary recursive solution requires a lot of stack space, on the order of the size of the blobs. Multiply that by the size of the stack frame, and you get horrible bytes/pixel requirements.
The scanline filling principle can reduce that requirement by orders of magnitude. Anyway, blob detection in textures ("porous" blobs) remains problematic.
You may also consider implementing this gem: "A Linear-Time Component-Labeling Algorithm Using Contour Tracing Technique, Fu Chang, Chun-Jen Chen, and Chi-Jen Lu."
The question is simple. How to draw following text into TStringGrid cell?
Operating system is Windows XP (or Windows Vista or Windows 7). Preferred development environment is C++ Builder 6, but I will accept also solutions for C++ Builder XE of Delphi. Preferred API function is DrawText, but if better function exists than this no problem. Font name is Times New Roman, font size is 11. Currently I am using this method to render cell content (simplified):
void __fastcall TForm_Main::StringGrid_DrawCell(TObject *Sender,
int ACol, int ARow, TRect &Rect, TGridDrawState State)
{
TStringGrid *grid = (TStringGrid*)Sender;
if (grid == NULL) return;
// 1. BACKGROUND
grid->Canvas->Brush->Color = grid->Color;
grid->Canvas->FillRect(Rect);
// 2. TEXT
grid->Canvas->Font->Assign(grid->Font); // Times New Roman, 11pt
// horizontal centering
RECT RText = static_cast<RECT>(Rect);
AnsiString text = grid->Cells[ACol][ARow];
if (!text.IsEmpty()) {
int text_len = strlen(text.c_str());
SIZE size;
memset(&size, 0, sizeof(SIZE));
GetTextExtentPoint32(grid->Canvas->Handle, text.c_str(), text_len, &size);
int offset_x = (Rect.Width() - size.cx) >> 1;
RText.left += offset_x; RText.right += offset_x;
// rendering
DrawText(grid->Canvas->Handle, text.c_str(), text_len, &RText, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
}
}
Some characters have subscript glyph as special unicode character (for example code 0x2081 is assigned to subscript one) but unfortunately this is not case for capital letter U. Also these characters are not supported by all fonts (for example Times New Roman font doesnt support code range 0x2070 - 209F), see this Wikipedia article. I am searching for a more general solution like those implemented by Microsoft Word. MS Word doesn't have problem to render capital letter U as subscript using Times New Roman font.
If you want some char to render as superscript you must prefix it with ^. Likewise subscript characters must be prefixed with _.
void __fastcall TForm_Main::StringGrid_DrawCell(TObject *Sender, int ACol, int ARow, TRect &Rect, TGridDrawState State)
{
TStringGrid *grid = (TStringGrid*)Sender;
if (grid == NULL)
{
return;
}
WideString wtext = L"φ_U = 120";
if (wtext.IsEmpty()) return;
// layout
SIZE size;
memset(&size, 0, sizeof(SIZE));
SSGetTextExtentPoint(grid->Canvas, wtext, size);
int offset_x = (Rect.Width() - size.cx + 1) >> 1; // horizontal centering
RECT RText = static_cast<RECT>(Rect);
RText.left += offset_x;
RText.right += offset_x;
// rendering
SetBkMode(grid->Canvas->Handle, TRANSPARENT);
SSDrawText(grid->Canvas, wtext, RText, DT_LEFT);
}
int TForm_Main::SSGetTextExtentPoint(TCanvas *canvas, WideString text, SIZE &size)
{
// Source: http://www.codeproject.com/Articles/12660/Using-Subscripts-and-Superscripts-When-Showing-Tex
SaveDC(canvas->Handle);
SIZE sz;
RECT outRect =
{0, 0, 0, 0};
HFONT oldFont;
LOGFONT lf;
GetObject(canvas->Font->Handle, sizeof(LOGFONT), &lf);
POINT sub, sup, subofs, supofs;
// Calculate subscript/superscript size and offsets
bool use_pixel_unit = false;
if (lf.lfHeight < 0)
{
lf.lfHeight = MulDiv(-lf.lfHeight, 72, GetDeviceCaps(canvas->Handle, LOGPIXELSY));
use_pixel_unit = true;
}
sub.x = lf.lfWidth / 2;
sup.x = lf.lfWidth / 2;
sub.y = lf.lfHeight / 3 * 2;
sup.y = lf.lfHeight / 3 * 2;
subofs.x = lf.lfWidth / 2;
supofs.x = lf.lfWidth / 2;
subofs.y = lf.lfHeight / 6;
supofs.y = lf.lfHeight / 3;
lf.lfWidth = sub.x;
lf.lfHeight = sub.y;
if (use_pixel_unit)
{
lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(canvas->Handle, LOGPIXELSY), 72);
}
HFONT SubFont;
SubFont = CreateFontIndirect(&lf);
lf.lfWidth = sup.x;
lf.lfHeight = sup.y;
HFONT SupFont;
if (use_pixel_unit)
{
lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(canvas->Handle, LOGPIXELSY), 72);
}
SupFont = CreateFontIndirect(&lf);
WideString temp = text;
TCHAR c;
// Calculate the size of the text that needs to be displayed
do
{
int x1 = 1, x2 = 1, x = 1;
WideString s = "";
c = ' ';
bool bFind = true;
// Find the first "^" or "_", indicating the sub- or superscript
while (bFind)
{
x1 = text.Pos(L"^");
x2 = text.Pos(L"_");
if ((x1 == 0) && (x2 == 0))
{
x = 0;
}
else if ((x1 > 0) && (x2 > 0))
{
x = min(x1, x2);
}
else if (x1 > 0)
{
x = x1;
}
else
{
x = x2;
}
if (x == 0)
{
bFind = false;
x = text.Length() + 1;
}
else if (x == text.Length())
{
bFind = false;
}
else if (text[x] != text[x + 1])
{
bFind = false;
c = text[x];
}
else
{
x++;
}
s = s + text.SubString(1, x - 1);
text.Delete(1, min(x, text.Length()));
}
sz = canvas->TextExtent(s);
outRect.right += sz.cx;
if ((outRect.bottom - outRect.top) < sz.cy)
{
outRect.top = outRect.bottom - sz.cy;
}
switch (c)
{
case '^':
oldFont = (HFONT)SelectObject(canvas->Handle, SupFont);
GetTextExtentPoint32W(canvas->Handle, text.c_bstr(), 1, &sz);
outRect.right += sz.cx + supofs.x;
text.Delete(1, 1);
SelectObject(canvas->Handle, oldFont);
break;
case '_':
oldFont = (HFONT)SelectObject(canvas->Handle, SubFont);
GetTextExtentPoint32W(canvas->Handle, text.c_bstr(), 1, &sz);
outRect.right += sz.cx + subofs.x;
text.Delete(1, 1);
SelectObject(canvas->Handle, oldFont);
break;
}
}
while (c != ' ');
// Adjust text position
outRect.bottom += subofs.y;
outRect.top -= subofs.x;
size.cx = outRect.right - outRect.left;
size.cy = outRect.bottom - outRect.top;
DeleteObject(SubFont);
DeleteObject(SupFont);
// Done, restoring the device context
RestoreDC(canvas->Handle, -1);
return 0;
}
// ---------------------------------------------------------------------------
int TForm_Main::SSDrawText(TCanvas *canvas, WideString text, RECT &drawRect, int justification)
{
// Source: http://www.codeproject.com/Articles/12660/Using-Subscripts-and-Superscripts-When-Showing-Tex
SaveDC(canvas->Handle);
SIZE sz;
RECT outRect =
{0, 0, 0, 0};
HFONT oldFont;
LOGFONT lf;
GetObject(canvas->Font->Handle, sizeof(LOGFONT), &lf);
POINT sub, sup, subofs, supofs;
bool contains_subscript = false;
bool contains_superscript = false;
// Calculate subscript/superscript size and offsets
bool use_pixel_unit = false;
if (lf.lfHeight < 0)
{
lf.lfHeight = MulDiv(-lf.lfHeight, 72, GetDeviceCaps(canvas->Handle, LOGPIXELSY));
use_pixel_unit = true;
}
sub.x = (lf.lfWidth + 1) >> 1;
sup.x = (lf.lfWidth + 1) >> 1;
sub.y = (lf.lfHeight << 1) / 3;
sup.y = (lf.lfHeight << 1) / 3;
if (lf.lfHeight == 10)
{
sub.y++; // make subscript a little larger
}
subofs.x = (lf.lfWidth + 1) >> 1;
supofs.x = (lf.lfWidth + 1) >> 1;
subofs.y = (lf.lfHeight + 3) / 6;
supofs.y = (lf.lfHeight) / 3;
long sub_shift_down = lf.lfHeight - sub.y;
lf.lfWidth = sub.x;
lf.lfHeight = sub.y;
if (use_pixel_unit)
{
lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(canvas->Handle, LOGPIXELSY), 72);
}
HFONT SubFont;
SubFont = CreateFontIndirect(&lf);
lf.lfWidth = sup.x;
lf.lfHeight = sup.y;
if (use_pixel_unit)
{
lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(canvas->Handle, LOGPIXELSY), 72);
}
HFONT SupFont;
SupFont = CreateFontIndirect(&lf);
WideString temp = text;
TCHAR c;
// Calculate the size of the text that needs to be displayed
do
{
int x1 = 1, x2 = 1, x = 1;
WideString s = "";
c = ' ';
bool bFind = true;
// Find the first "^" or "_", indicating the sub- or superscript
while (bFind)
{
x1 = text.Pos(L"^");
x2 = text.Pos(L"_");
if ((x1 == 0) && (x2 == 0))
{
x = 0;
}
else if ((x1 > 0) && (x2 > 0))
{
x = min(x1, x2);
}
else if (x1 > 0)
{
x = x1;
}
else
{
x = x2;
}
if (x == 0)
{
bFind = false;
x = text.Length() + 1;
}
else if (x == text.Length())
{
bFind = false;
}
else if (text[x] != text[x + 1])
{
bFind = false;
c = text[x];
}
else
{
x++;
}
s = s + text.SubString(1, x - 1);
text.Delete(1, min(x, text.Length()));
}
sz = canvas->TextExtent(s);
outRect.right += sz.cx;
if ((outRect.bottom - outRect.top) < sz.cy)
{
outRect.top = outRect.bottom - sz.cy;
}
switch (c)
{
case '^':
oldFont = (HFONT)SelectObject(canvas->Handle, SupFont);
GetTextExtentPoint32W(canvas->Handle, text.c_bstr(), 1, &sz);
outRect.right += sz.cx + supofs.x;
text.Delete(1, 1);
SelectObject(canvas->Handle, oldFont);
contains_superscript = true;
break;
case '_':
oldFont = (HFONT)SelectObject(canvas->Handle, SubFont);
GetTextExtentPoint32W(canvas->Handle, text.c_bstr(), 1, &sz);
outRect.right += sz.cx + subofs.x;
text.Delete(1, 1);
SelectObject(canvas->Handle, oldFont);
contains_subscript = true;
break;
}
}
while (c != ' ');
// Adjust text position
if (contains_subscript)
{
outRect.bottom += subofs.y;
}
if (contains_superscript)
{
outRect.top -= supofs.y;
}
POINT Origin;
Origin.y = drawRect.top + (((drawRect.bottom - drawRect.top) - (outRect.bottom - outRect.top) + 1) >> 1);
switch (justification)
{
case DT_CENTER:
Origin.x = (drawRect.right - drawRect.left) / 2 - (outRect.right - outRect.left) / 2 + drawRect.left;
break;
case DT_LEFT:
Origin.x = drawRect.left;
break;
case DT_RIGHT:
Origin.x = drawRect.right - (outRect.right - outRect.left);
}
POINT pnt = Origin;
text = temp;
// Draw text
do
{
int x1 = 1, x2 = 1, x = 1;
WideString s = "";
c = ' ';
bool bFind = true;
// Find the first "^" or "_", indicating the sub- or superscript
while (bFind)
{
x = text.Pos(L"^_");
x1 = text.Pos(L"^");
x2 = text.Pos(L"_");
if ((x1 == 0) && (x2 == 0))
{
x = 0;
}
else if ((x1 > 0) && (x2 > 0))
{
x = min(x1, x2);
}
else if (x1 > 0)
{
x = x1;
}
else
{
x = x2;
}
if (x == 0)
{
bFind = false;
x = text.Length() + 1;
}
else if (x == text.Length())
{
bFind = false;
}
else if (text[x] != text[x + 1])
{
bFind = false;
c = text[x];
}
else
{
x++;
}
s = s + text.SubString(1, x - 1);
text.Delete(1, min(x, text.Length()));
}
// Draw main text
ExtTextOutW(canvas->Handle, pnt.x, pnt.y, 0, &drawRect, s.c_bstr(), s.Length(), NULL);
GetTextExtentPoint32W(canvas->Handle, s.c_bstr(), s.Length(), &sz);
pnt.x += sz.cx;
// Draw subscript or superscript
switch (c)
{
case '^':
oldFont = (HFONT)SelectObject(canvas->Handle, SupFont);
ExtTextOutW(canvas->Handle, pnt.x + supofs.x, pnt.y - supofs.y, 0, &drawRect, text.c_bstr(), 1, NULL);
GetTextExtentPoint32W(canvas->Handle, text.c_bstr(), 1, &sz);
pnt.x += sz.cx + supofs.x;
text.Delete(1, 1);
SelectObject(canvas->Handle, oldFont);
break;
case '_':
oldFont = (HFONT)SelectObject(canvas->Handle, SubFont);
ExtTextOutW(canvas->Handle, pnt.x + subofs.x, pnt.y + subofs.y + sub_shift_down, 0, &drawRect, text.c_bstr(), 1, NULL);
GetTextExtentPoint32W(canvas->Handle, text.c_bstr(), 1, &sz);
pnt.x += sz.cx + subofs.x;
text.Delete(1, 1);
SelectObject(canvas->Handle, oldFont);
break;
}
}
while (c != ' ');
DeleteObject(SubFont);
DeleteObject(SupFont);
// Done, restoring the device context
RestoreDC(canvas->Handle, -1);
return 0;
}
`
function SSTextLeft(ACanvas: TCanvas; ARect: TRect; const S: string): integer;
var
i,
h,
h3: integer;
sup,
sub: boolean;
begin
with ACanvas, ARect do begin
h:= textHeight('H');
h3:= h div 3;
brush.Style:= bsClear;
moveTo(Left +1, Top +1);
i:= 1;
while i < Length(s) +1 do begin
if s[i] = '^' then
begin
Inc(i);
font.Height:= MulDiv(font.Height, 8, 10);
textOut(penPos.X, penPos.Y -h3, s[i]);
moveTo(penPos.X +1, penPos.Y +h3);
font.Height:= h;
Inc(i);
end
else if s[i] = '_' then
begin
Inc(i);
font.Height:= MulDiv(font.Height, 8, 10);
textOut(penPos.X, penPos.Y +h3, s[i]);
moveTo(penPos.X +1, penPos.Y -h3);
font.Height:= h;
Inc(i);
end
else
begin
textOut(penPos.X, penPos.Y, s[i]);
Inc(i);
moveTo(penPos.X +1, penPos.Y);
end;
end;// while
end;
sup:= Pos('^', S) > 0;
sub:= Pos('_', S) > 0;
if sup and sub then
result:= MulDiv(h, 5, 3)
else if sup or sub then
result:= h +h3
else
result:= h;
end;// SSTextLeft
`