Fix T44541 aka gigapixel image render support in blender.

Moral of the story: Make sure that size_t is used whenever pointer
arithmetic is involved. For images, that basically means whenever any
squared dimensions are involved. Casting an operand to size_t early in
the operation is usually sufficient to force the entire operation to
size_t.

There might still be places lurking where we don't support this
correctly. This has been tested with render pipeline, quite a few image
functions (meaning we can paint on such images now, albeit somewhat
slowly ;) ) and export to jpeg. Too many places in code to check so I
guess we'll be handling cases as they come.

Don't try this at home unless you have an immense ammount of RAM.
First GPixel render of suzanne in the multiverse can be found here:

http://download.blender.org/demo/test/suzanne-billion-pixel.jpg

Can be viewed from blender (takes about 3.3 GB after loading but may
take more during loading so 8GB might be more safe to try this).
This commit is contained in:
Antonis Ryakiotakis 2015-04-30 12:10:58 +02:00
parent 4bcc7a2ec6
commit 2909975385
Notes: blender-bot 2023-02-14 09:11:59 +01:00
Referenced by issue #44541, Blender crashes when outputting large images
7 changed files with 78 additions and 77 deletions

View File

@ -579,25 +579,25 @@ void glaDrawPixelsTexScaled(float x, float y, int img_w, int img_h, int format,
continue;
if (type == GL_FLOAT) {
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, subpart_w, subpart_h, format, GL_FLOAT, &f_rect[subpart_y * offset_y * img_w * components + subpart_x * offset_x * components]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, subpart_w, subpart_h, format, GL_FLOAT, &f_rect[((size_t)subpart_y) * offset_y * img_w * components + subpart_x * offset_x * components]);
/* add an extra border of pixels so linear looks ok at edges of full image. */
if (subpart_w < tex_w)
glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, 0, 1, subpart_h, format, GL_FLOAT, &f_rect[subpart_y * offset_y * img_w * components + (subpart_x * offset_x + subpart_w - 1) * components]);
glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, 0, 1, subpart_h, format, GL_FLOAT, &f_rect[((size_t)subpart_y) * offset_y * img_w * components + (subpart_x * offset_x + subpart_w - 1) * components]);
if (subpart_h < tex_h)
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, subpart_h, subpart_w, 1, format, GL_FLOAT, &f_rect[(subpart_y * offset_y + subpart_h - 1) * img_w * components + subpart_x * offset_x * components]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, subpart_h, subpart_w, 1, format, GL_FLOAT, &f_rect[(((size_t)subpart_y) * offset_y + subpart_h - 1) * img_w * components + subpart_x * offset_x * components]);
if (subpart_w < tex_w && subpart_h < tex_h)
glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, subpart_h, 1, 1, format, GL_FLOAT, &f_rect[(subpart_y * offset_y + subpart_h - 1) * img_w * components + (subpart_x * offset_x + subpart_w - 1) * components]);
glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, subpart_h, 1, 1, format, GL_FLOAT, &f_rect[(((size_t)subpart_y) * offset_y + subpart_h - 1) * img_w * components + (subpart_x * offset_x + subpart_w - 1) * components]);
}
else {
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, subpart_w, subpart_h, format, GL_UNSIGNED_BYTE, &uc_rect[subpart_y * offset_y * img_w * components + subpart_x * offset_x * components]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, subpart_w, subpart_h, format, GL_UNSIGNED_BYTE, &uc_rect[((size_t)subpart_y) * offset_y * img_w * components + subpart_x * offset_x * components]);
if (subpart_w < tex_w)
glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, 0, 1, subpart_h, format, GL_UNSIGNED_BYTE, &uc_rect[subpart_y * offset_y * img_w * components + (subpart_x * offset_x + subpart_w - 1) * components]);
glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, 0, 1, subpart_h, format, GL_UNSIGNED_BYTE, &uc_rect[((size_t)subpart_y) * offset_y * img_w * components + (subpart_x * offset_x + subpart_w - 1) * components]);
if (subpart_h < tex_h)
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, subpart_h, subpart_w, 1, format, GL_UNSIGNED_BYTE, &uc_rect[(subpart_y * offset_y + subpart_h - 1) * img_w * components + subpart_x * offset_x * components]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, subpart_h, subpart_w, 1, format, GL_UNSIGNED_BYTE, &uc_rect[(((size_t)subpart_y) * offset_y + subpart_h - 1) * img_w * components + subpart_x * offset_x * components]);
if (subpart_w < tex_w && subpart_h < tex_h)
glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, subpart_h, 1, 1, format, GL_UNSIGNED_BYTE, &uc_rect[(subpart_y * offset_y + subpart_h - 1) * img_w * components + (subpart_x * offset_x + subpart_w - 1) * components]);
glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, subpart_h, 1, 1, format, GL_UNSIGNED_BYTE, &uc_rect[(((size_t)subpart_y) * offset_y + subpart_h - 1) * img_w * components + (subpart_x * offset_x + subpart_w - 1) * components]);
}
glEnable(GL_TEXTURE_2D);

View File

@ -465,10 +465,10 @@ ImBuf *IMB_dupImBuf(ImBuf *ibuf1)
if (ibuf2 == NULL) return NULL;
if (flags & IB_rect)
memcpy(ibuf2->rect, ibuf1->rect, x * y * sizeof(int));
memcpy(ibuf2->rect, ibuf1->rect, ((size_t)x) * y * sizeof(int));
if (flags & IB_rectfloat)
memcpy(ibuf2->rect_float, ibuf1->rect_float, ibuf1->channels * x * y * sizeof(float));
memcpy(ibuf2->rect_float, ibuf1->rect_float, ((size_t)ibuf1->channels) * x * y * sizeof(float));
if (ibuf1->encodedbuffer) {
ibuf2->encodedbuffersize = ibuf1->encodedbuffersize;

View File

@ -1306,8 +1306,8 @@ static void display_buffer_init_handle(void *handle_v, int start_line, int tot_l
float dither = ibuf->dither;
bool is_data = (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) != 0;
int offset = channels * start_line * ibuf->x;
int display_buffer_byte_offset = DISPLAY_BUFFER_CHANNELS * start_line * ibuf->x;
size_t offset = ((size_t)channels) * start_line * ibuf->x;
size_t display_buffer_byte_offset = ((size_t)DISPLAY_BUFFER_CHANNELS) * start_line * ibuf->x;
memset(handle, 0, sizeof(DisplayBufferThread));
@ -1344,7 +1344,7 @@ static void display_buffer_apply_get_linear_buffer(DisplayBufferThread *handle,
int channels = handle->channels;
int width = handle->width;
int buffer_size = channels * width * height;
size_t buffer_size = ((size_t)channels) * width * height;
bool is_data = handle->is_data;
bool is_data_display = handle->cm_processor->is_data_result;
@ -1357,11 +1357,11 @@ static void display_buffer_apply_get_linear_buffer(DisplayBufferThread *handle,
float *fp;
unsigned char *cp;
int i;
size_t i;
/* first convert byte buffer to float, keep in image space */
for (i = 0, fp = linear_buffer, cp = byte_buffer;
i < width * height;
i < ((size_t)width) * height;
i++, fp += channels, cp += channels)
{
if (channels == 3) {
@ -1440,7 +1440,7 @@ static void *do_display_buffer_apply_thread(void *handle_v)
}
else {
bool is_straight_alpha, predivide;
float *linear_buffer = MEM_mallocN(channels * width * height * sizeof(float),
float *linear_buffer = MEM_mallocN(((size_t)channels) * width * height * sizeof(float),
"color conversion linear buffer");
display_buffer_apply_get_linear_buffer(handle, height, linear_buffer, &is_straight_alpha);
@ -1467,14 +1467,14 @@ static void *do_display_buffer_apply_thread(void *handle_v)
}
if (display_buffer) {
memcpy(display_buffer, linear_buffer, width * height * channels * sizeof(float));
memcpy(display_buffer, linear_buffer, ((size_t)width) * height * channels * sizeof(float));
if (is_straight_alpha && channels == 4) {
int i;
size_t i;
float *fp;
for (i = 0, fp = display_buffer;
i < width * height;
i < ((size_t)width) * height;
i++, fp += channels)
{
straight_to_premul_v4(fp);
@ -2959,7 +2959,7 @@ void IMB_colormanagement_processor_apply(ColormanageProcessor *cm_processor, flo
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
float *pixel = buffer + channels * (y * width + x);
float *pixel = buffer + channels * (((size_t)y) * width + x);
curve_mapping_apply_pixel(cm_processor->curve_mapping, pixel, channels);
}

View File

@ -184,16 +184,16 @@ void IMB_buffer_byte_from_float(uchar *rect_to, const float *rect_from,
if (channels_from == 1) {
/* single channel input */
const float *from = rect_from + stride_from * y;
uchar *to = rect_to + stride_to * y * 4;
const float *from = rect_from + ((size_t)stride_from) * y;
uchar *to = rect_to + ((size_t)stride_to) * y * 4;
for (x = 0; x < width; x++, from++, to += 4)
to[0] = to[1] = to[2] = to[3] = FTOCHAR(from[0]);
}
else if (channels_from == 3) {
/* RGB input */
const float *from = rect_from + stride_from * y * 3;
uchar *to = rect_to + stride_to * y * 4;
const float *from = rect_from + ((size_t)stride_from) * y * 3;
uchar *to = rect_to + ((size_t)stride_to) * y * 4;
if (profile_to == profile_from) {
/* no color space conversion */
@ -221,8 +221,8 @@ void IMB_buffer_byte_from_float(uchar *rect_to, const float *rect_from,
}
else if (channels_from == 4) {
/* RGBA input */
const float *from = rect_from + stride_from * y * 4;
uchar *to = rect_to + stride_to * y * 4;
const float *from = rect_from + ((size_t)stride_from) * y * 4;
uchar *to = rect_to + ((size_t)stride_to) * y * 4;
if (profile_to == profile_from) {
float straight[4];
@ -334,8 +334,8 @@ void IMB_buffer_byte_from_float_mask(uchar *rect_to, const float *rect_from,
if (channels_from == 1) {
/* single channel input */
const float *from = rect_from + stride_from * y;
uchar *to = rect_to + stride_to * y * 4;
const float *from = rect_from + ((size_t)stride_from) * y;
uchar *to = rect_to + ((size_t)stride_to) * y * 4;
for (x = 0; x < width; x++, from++, to += 4)
if (*mask++ == FILTER_MASK_USED)
@ -343,8 +343,8 @@ void IMB_buffer_byte_from_float_mask(uchar *rect_to, const float *rect_from,
}
else if (channels_from == 3) {
/* RGB input */
const float *from = rect_from + stride_from * y * 3;
uchar *to = rect_to + stride_to * y * 4;
const float *from = rect_from + ((size_t)stride_from) * y * 3;
uchar *to = rect_to + ((size_t)stride_to) * y * 4;
for (x = 0; x < width; x++, from += 3, to += 4) {
if (*mask++ == FILTER_MASK_USED) {
@ -355,8 +355,8 @@ void IMB_buffer_byte_from_float_mask(uchar *rect_to, const float *rect_from,
}
else if (channels_from == 4) {
/* RGBA input */
const float *from = rect_from + stride_from * y * 4;
uchar *to = rect_to + stride_to * y * 4;
const float *from = rect_from + ((size_t)stride_from) * y * 4;
uchar *to = rect_to + ((size_t)stride_to) * y * 4;
float straight[4];
@ -408,7 +408,7 @@ void IMB_buffer_float_from_byte(float *rect_to, const uchar *rect_from,
/* RGBA input */
for (y = 0; y < height; y++) {
const uchar *from = rect_from + stride_from * y * 4;
float *to = rect_to + stride_to * y * 4;
float *to = rect_to + ((size_t)stride_to) * y * 4;
if (profile_to == profile_from) {
/* no color space conversion */
@ -460,8 +460,8 @@ void IMB_buffer_float_from_float(float *rect_to, const float *rect_from,
if (channels_from == 1) {
/* single channel input */
for (y = 0; y < height; y++) {
const float *from = rect_from + stride_from * y;
float *to = rect_to + stride_to * y * 4;
const float *from = rect_from + ((size_t)stride_from) * y;
float *to = rect_to + ((size_t)stride_to) * y * 4;
for (x = 0; x < width; x++, from++, to += 4)
to[0] = to[1] = to[2] = to[3] = from[0];
@ -470,8 +470,8 @@ void IMB_buffer_float_from_float(float *rect_to, const float *rect_from,
else if (channels_from == 3) {
/* RGB input */
for (y = 0; y < height; y++) {
const float *from = rect_from + stride_from * y * 3;
float *to = rect_to + stride_to * y * 4;
const float *from = rect_from + ((size_t)stride_from) * y * 3;
float *to = rect_to + ((size_t)stride_to) * y * 4;
if (profile_to == profile_from) {
/* no color space conversion */
@ -499,12 +499,12 @@ void IMB_buffer_float_from_float(float *rect_to, const float *rect_from,
else if (channels_from == 4) {
/* RGBA input */
for (y = 0; y < height; y++) {
const float *from = rect_from + stride_from * y * 4;
float *to = rect_to + stride_to * y * 4;
const float *from = rect_from + ((size_t)stride_from) * y * 4;
float *to = rect_to + ((size_t)stride_to) * y * 4;
if (profile_to == profile_from) {
/* same profile, copy */
memcpy(to, from, sizeof(float) * 4 * width);
memcpy(to, from, sizeof(float) * ((size_t)4) * width);
}
else if (profile_to == IB_PROFILE_LINEAR_RGB) {
/* convert to sRGB to linear */
@ -541,8 +541,8 @@ void IMB_buffer_float_from_float_mask(float *rect_to, const float *rect_from, in
if (channels_from == 1) {
/* single channel input */
for (y = 0; y < height; y++) {
const float *from = rect_from + stride_from * y;
float *to = rect_to + stride_to * y * 4;
const float *from = rect_from + ((size_t)stride_from) * y;
float *to = rect_to + ((size_t)stride_to) * y * 4;
for (x = 0; x < width; x++, from++, to += 4)
if (*mask++ == FILTER_MASK_USED)
@ -552,8 +552,8 @@ void IMB_buffer_float_from_float_mask(float *rect_to, const float *rect_from, in
else if (channels_from == 3) {
/* RGB input */
for (y = 0; y < height; y++) {
const float *from = rect_from + stride_from * y * 3;
float *to = rect_to + stride_to * y * 4;
const float *from = rect_from + ((size_t)stride_from) * y * 3;
float *to = rect_to + ((size_t)stride_to) * y * 4;
for (x = 0; x < width; x++, from += 3, to += 4) {
if (*mask++ == FILTER_MASK_USED) {
@ -566,8 +566,8 @@ void IMB_buffer_float_from_float_mask(float *rect_to, const float *rect_from, in
else if (channels_from == 4) {
/* RGBA input */
for (y = 0; y < height; y++) {
const float *from = rect_from + stride_from * y * 4;
float *to = rect_to + stride_to * y * 4;
const float *from = rect_from + ((size_t)stride_from) * y * 4;
float *to = rect_to + ((size_t)stride_to) * y * 4;
for (x = 0; x < width; x++, from += 4, to += 4)
if (*mask++ == FILTER_MASK_USED)
@ -590,8 +590,8 @@ void IMB_buffer_byte_from_byte(uchar *rect_to, const uchar *rect_from,
/* always RGBA input */
for (y = 0; y < height; y++) {
const uchar *from = rect_from + stride_from * y * 4;
uchar *to = rect_to + stride_to * y * 4;
const uchar *from = rect_from + ((size_t)stride_from) * y * 4;
uchar *to = rect_to + ((size_t)stride_to) * y * 4;
if (profile_to == profile_from) {
/* same profile, copy */
@ -690,8 +690,8 @@ void IMB_partial_rect_from_float(ImBuf *ibuf, float *buffer, int x, int y, int w
imb_addrectImBuf(ibuf);
/* do conversion */
rect_float = ibuf->rect_float + (x + y * ibuf->x) * ibuf->channels;
rect_byte = (uchar *)ibuf->rect + (x + y * ibuf->x) * 4;
rect_float = ibuf->rect_float + (x + ((size_t)y) * ibuf->x) * ibuf->channels;
rect_byte = (uchar *)ibuf->rect + (x + ((size_t)y) * ibuf->x) * 4;
if (is_data) {
/* exception for non-color data, just copy float */
@ -734,9 +734,9 @@ void IMB_float_from_rect(ImBuf *ibuf)
*/
rect_float = ibuf->rect_float;
if (rect_float == NULL) {
int size;
size_t size;
size = ibuf->x * ibuf->y;
size = ((size_t)ibuf->x) * ibuf->y;
size = size * 4 * sizeof(float);
ibuf->channels = 4;
@ -771,22 +771,22 @@ void IMB_color_to_bw(ImBuf *ibuf)
{
float *rct_fl = ibuf->rect_float;
uchar *rct = (uchar *)ibuf->rect;
int i;
size_t i;
if (rct_fl) {
for (i = ibuf->x * ibuf->y; i > 0; i--, rct_fl += 4)
for (i = ((size_t)ibuf->x) * ibuf->y; i > 0; i--, rct_fl += 4)
rct_fl[0] = rct_fl[1] = rct_fl[2] = IMB_colormanagement_get_luminance(rct_fl);
}
if (rct) {
for (i = ibuf->x * ibuf->y; i > 0; i--, rct += 4)
for (i = ((size_t)ibuf->x * ibuf->y); i > 0; i--, rct += 4)
rct[0] = rct[1] = rct[2] = IMB_colormanagement_get_luminance_byte(rct);
}
}
void IMB_buffer_float_clamp(float *buf, int width, int height)
{
int i, total = width * height * 4;
size_t i, total = ((size_t)width) * height * 4;
for (i = 0; i < total; i++) {
buf[i] = min_ff(1.0, buf[i]);
}
@ -794,7 +794,7 @@ void IMB_buffer_float_clamp(float *buf, int width, int height)
void IMB_buffer_float_unpremultiply(float *buf, int width, int height)
{
int total = width * height;
size_t total = ((size_t)width) * height;
float *fp = buf;
while (total--) {
premul_to_straight_v4(fp);
@ -804,7 +804,7 @@ void IMB_buffer_float_unpremultiply(float *buf, int width, int height)
void IMB_buffer_float_premultiply(float *buf, int width, int height)
{
int total = width * height;
size_t total = ((size_t)width) * height;
float *fp = buf;
while (total--) {
straight_to_premul_v4(fp);
@ -816,14 +816,14 @@ void IMB_buffer_float_premultiply(float *buf, int width, int height)
void IMB_saturation(ImBuf *ibuf, float sat)
{
int i;
size_t i;
unsigned char *rct = (unsigned char *)ibuf->rect;
float *rct_fl = ibuf->rect_float;
float hsv[3];
if (rct) {
float rgb[3];
for (i = ibuf->x * ibuf->y; i > 0; i--, rct += 4) {
for (i = ((size_t)ibuf->x) * ibuf->y; i > 0; i--, rct += 4) {
rgb_uchar_to_float(rgb, rct);
rgb_to_hsv_v(rgb, hsv);
hsv_to_rgb(hsv[0], hsv[1] * sat, hsv[2], rgb, rgb + 1, rgb + 2);
@ -832,7 +832,7 @@ void IMB_saturation(ImBuf *ibuf, float sat)
}
if (rct_fl) {
for (i = ibuf->x * ibuf->y; i > 0; i--, rct_fl += 4) {
for (i = ((size_t)ibuf->x) * ibuf->y; i > 0; i--, rct_fl += 4) {
rgb_to_hsv_v(rct_fl, hsv);
hsv_to_rgb(hsv[0], hsv[1] * sat, hsv[2], rct_fl, rct_fl + 1, rct_fl + 2);
}

View File

@ -343,7 +343,7 @@ void IMB_processor_apply_threaded(int buffer_lines, int handle_size, void *init_
void IMB_alpha_under_color_float(float *rect_float, int x, int y, float backcol[3])
{
int a = x * y;
size_t a = ((size_t)x) * y;
float *fp = rect_float;
while (a--) {
@ -366,7 +366,7 @@ void IMB_alpha_under_color_float(float *rect_float, int x, int y, float backcol[
void IMB_alpha_under_color_byte(unsigned char *rect, int x, int y, float backcol[3])
{
int a = x * y;
size_t a = ((size_t)x) * y;
unsigned char *cp = rect;
while (a--) {

View File

@ -322,30 +322,30 @@ void IMB_rectblend(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf, unsigned short *dmask,
do_float = (sbuf && sbuf->rect_float && dbuf->rect_float && obuf->rect_float);
if (do_char) {
drect = dbuf->rect + desty * dbuf->x + destx;
orect = obuf->rect + origy * obuf->x + origx;
drect = dbuf->rect + ((size_t)desty) * dbuf->x + destx;
orect = obuf->rect + ((size_t)origy) * obuf->x + origx;
}
if (do_float) {
drectf = dbuf->rect_float + (desty * dbuf->x + destx) * 4;
orectf = obuf->rect_float + (origy * obuf->x + origx) * 4;
drectf = dbuf->rect_float + (((size_t)desty) * dbuf->x + destx) * 4;
orectf = obuf->rect_float + (((size_t)origy) * obuf->x + origx) * 4;
}
if (dmaskrect)
dmaskrect += origy * obuf->x + origx;
dmaskrect += ((size_t)origy) * obuf->x + origx;
destskip = dbuf->x;
origskip = obuf->x;
if (sbuf) {
if (do_char) srect = sbuf->rect + srcy * sbuf->x + srcx;
if (do_float) srectf = sbuf->rect_float + (srcy * sbuf->x + srcx) * 4;
if (do_char) srect = sbuf->rect + ((size_t)srcy) * sbuf->x + srcx;
if (do_float) srectf = sbuf->rect_float + (((size_t)srcy) * sbuf->x + srcx) * 4;
srcskip = sbuf->x;
if (cmaskrect)
cmaskrect += srcy * sbuf->x + srcx;
cmaskrect += ((size_t)srcy) * sbuf->x + srcx;
if (texmaskrect)
texmaskrect += srcy * sbuf->x + srcx;
texmaskrect += ((size_t)srcy) * sbuf->x + srcx;
}
else {
srect = drect;

View File

@ -486,7 +486,7 @@ static RenderPass *render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int
const size_t view_id = BLI_findstringindex(&rr->views, viewname, offsetof(RenderView, name));
const char *typestr = name_from_passtype(passtype, -1);
RenderPass *rpass = MEM_callocN(sizeof(RenderPass), typestr);
size_t rectsize = ((size_t)rr->rectx) * ((size_t)rr->recty) * ((size_t)channels);
size_t rectsize = ((size_t)rr->rectx) * rr->recty * channels;
BLI_addtail(&rl->passes, rpass);
rpass->passtype = passtype;
@ -944,22 +944,23 @@ void render_result_views_new(RenderResult *rr, RenderData *rd)
static void do_merge_tile(RenderResult *rr, RenderResult *rrpart, float *target, float *tile, int pixsize)
{
int y, ofs, copylen, tilex, tiley;
int y, tilex, tiley;
size_t ofs, copylen;
copylen = tilex = rrpart->rectx;
tiley = rrpart->recty;
if (rrpart->crop) { /* filters add pixel extra */
tile += pixsize * (rrpart->crop + rrpart->crop * tilex);
tile += pixsize * (rrpart->crop + ((size_t)rrpart->crop) * tilex);
copylen = tilex - 2 * rrpart->crop;
tiley -= 2 * rrpart->crop;
ofs = (rrpart->tilerect.ymin + rrpart->crop) * rr->rectx + (rrpart->tilerect.xmin + rrpart->crop);
ofs = (((size_t)rrpart->tilerect.ymin) + rrpart->crop) * rr->rectx + (rrpart->tilerect.xmin + rrpart->crop);
target += pixsize * ofs;
}
else {
ofs = (rrpart->tilerect.ymin * rr->rectx + rrpart->tilerect.xmin);
ofs = (((size_t)rrpart->tilerect.ymin) * rr->rectx + rrpart->tilerect.xmin);
target += pixsize * ofs;
}