1 /* 2 3 Copyright 1989, 1998 The Open Group 4 5 Permission to use, copy, modify, distribute, and sell this software and its 6 documentation for any purpose is hereby granted without fee, provided that 7 the above copyright notice appear in all copies and that both that 8 copyright notice and this permission notice appear in supporting 9 documentation. 10 11 The above copyright notice and this permission notice shall be included 12 in all copies or substantial portions of the Software. 13 14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 OTHER DEALINGS IN THE SOFTWARE. 21 22 Except as contained in this notice, the name of The Open Group shall 23 not be used in advertising or otherwise to promote the sale, use or 24 other dealings in this Software without prior written authorization 25 from The Open Group. 26 27 */ 28 29 /* 30 * Author: Davor Matic, MIT X Consortium 31 */ 32 33 #ifdef HAVE_CONFIG_H 34 #include "config.h" 35 #endif 36 37 #include <stdio.h> 38 #include <ctype.h> 39 #include <math.h> 40 #include <stdlib.h> 41 42 #include <X11/IntrinsicP.h> 43 #include <X11/StringDefs.h> 44 #include <X11/Xos.h> 45 #include <X11/Xaw/XawInit.h> 46 47 #include "CutPaste.h" 48 #include "ScaleP.h" 49 50 #ifdef HAVE_LRINT 51 #define myrint(x) lrint(x) 52 #else 53 #define myrint(x) floor(x + 0.5) 54 #endif 55 56 #define streq(a,b) (strcmp( (a), (b) ) == 0) 57 #ifndef min 58 #define min(x, y) ((x) > (y) ? (y) : (x)) 59 #endif 60 #ifndef max 61 #define max(x, y) ((x) < (y) ? (y) : (x)) 62 #endif 63 64 #define DefaultBufferSize 1024 65 #define DefaultScaleFactor NULL 66 67 #define Offset(field) XtOffsetOf(ScaleRec, scale.field) 68 69 static XtResource resources[] = { 70 {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel), 71 Offset(foreground_pixel), XtRString, (XtPointer) XtDefaultForeground}, 72 {XtNgravity, XtCGravity, XtRGravity, sizeof(XtGravity), 73 Offset(gravity), XtRImmediate, (XtPointer) "ForgetGravity"}, 74 {XtNinternalWidth, XtCWidth, XtRDimension, sizeof(Dimension), 75 Offset(internal_width), XtRImmediate, (XtPointer) 2}, 76 {XtNinternalHeight, XtCHeight, XtRDimension, sizeof(Dimension), 77 Offset(internal_height), XtRImmediate, (XtPointer) 2}, 78 {XtNresize, XtCResize, XtRBoolean, sizeof(Boolean), 79 Offset(resize), XtRImmediate, (XtPointer) True}, 80 {XtNautoscale, XtCAutoscale, XtRBoolean, sizeof(Boolean), 81 Offset(autoscale), XtRImmediate, (XtPointer) True}, 82 {XtNproportional, XtCProportional, XtRBoolean, sizeof(Boolean), 83 Offset(proportional), XtRImmediate, (XtPointer) True}, 84 {XtNscaleX, XtCScaleFactor, XtRString, sizeof(String), 85 Offset(scale_x_str), XtRImmediate, (XtPointer) DefaultScaleFactor}, 86 {XtNscaleY, XtCScaleFactor, XtRString, sizeof(String), 87 Offset(scale_y_str), XtRImmediate, (XtPointer) DefaultScaleFactor}, 88 {XtNaspectRatio, XtCAspectRatio, XtRString, sizeof(String), 89 Offset(aspect_ratio_str), XtRImmediate, (XtPointer) "1.0"}, 90 {XtNprecision, XtCPrecision, XtRString, sizeof(String), 91 Offset(precision_str), XtRImmediate, (XtPointer) "0.001"}, 92 {XtNimage, XtCImage, XtRImage, sizeof(XImage*), 93 Offset(image), XtRImmediate, (XtPointer) NULL}, 94 {XtNpasteBuffer, XtCPasteBuffer, XtRBoolean, sizeof(Boolean), 95 Offset(paste_buffer), XtRImmediate, (XtPointer) False}, 96 {XtNbufferSize, XtCBufferSize, XtRCardinal, sizeof(Cardinal), 97 Offset(buffer_size), XtRImmediate, (XtPointer) DefaultBufferSize}, 98 {XtNuserData, XtCuserData, XtRuserData, sizeof(XtPointer), 99 Offset(userData), XtRImmediate, (XtPointer) NULL}, 100 { XtNvisual, XtCvisual, XtRVisual, sizeof(Visual*), 101 Offset(visual), XtRImmediate, CopyFromParent} 102 }; 103 104 #undef Offset 105 106 static void ClassInitialize ( void ); 107 static void GetGC ( ScaleWidget sw ); 108 static void GetInitialScaleValues ( ScaleWidget sw ); 109 static void GetRectangleBuffer ( ScaleWidget sw, Cardinal buffer_size ); 110 static void Initialize ( Widget request, Widget new, ArgList args, 111 Cardinal *num_args ); 112 static void BuildTable ( ScaleWidget sw ); 113 static void FlushRectangles ( ScaleWidget sw, Drawable drawable, GC gc ); 114 static void FillRectangle ( ScaleWidget sw, Drawable drawable, GC gc, 115 Position x, Position y, 116 Dimension width, Dimension height ); 117 static void ScaleImage ( ScaleWidget sw, Drawable drawable, 118 Position img_x, Position img_y, 119 Position dst_x, Position dst_y, 120 Dimension img_width, Dimension img_height ); 121 static int FindPixel ( ScaleWidget sw, Position x, Position y, 122 Position *img_x, Position *img_y, Pixel *img_pixel ); 123 static void Redisplay ( Widget w, XEvent *event, Region region ); 124 static void TryResize ( ScaleWidget sw ); 125 static void Precision ( ScaleWidget sw ); 126 static void Proportional ( ScaleWidget sw ); 127 static void GetScaledSize ( ScaleWidget sw ); 128 static void GetScaleValues ( ScaleWidget sw ); 129 static void Unscale ( ScaleWidget sw ); 130 static void Autoscale ( ScaleWidget sw ); 131 static void PositionImage ( ScaleWidget sw ); 132 static void Resize ( Widget w ); 133 static void Realize ( Widget wid, Mask *vmask, XSetWindowAttributes *attr ); 134 static void Destroy ( Widget w ); 135 static Boolean SetValues ( Widget current, Widget request, Widget new, 136 ArgList args, Cardinal *num_args ); 137 138 139 140 static XtActionsRec actions[] = 141 { 142 {"unscale", SWUnscale}, 143 {"autoscale", SWAutoscale}, 144 {"initial-size", SWInitialSize}, 145 {"paste", RequestSelection}, 146 {"cut", GrabSelection} 147 }; 148 149 static char translations[] = 150 "\ 151 <Key>u: unscale()\n\ 152 <Key>a: autoscale()\n\ 153 <Key>i: initial-size()\n\ 154 "; 155 156 ScaleClassRec scaleClassRec = { 157 { /* core fields */ 158 /* superclass */ (WidgetClass) &simpleClassRec, 159 /* class_name */ "Scale", 160 /* widget_size */ sizeof(ScaleRec), 161 /* class_initialize */ ClassInitialize, 162 /* class_part_initialize */ NULL, 163 /* class_inited */ FALSE, 164 /* initialize */ Initialize, 165 /* initialize_hook */ NULL, 166 /* realize */ Realize, 167 /* actions */ actions, 168 /* num_actions */ XtNumber(actions), 169 /* resources */ resources, 170 /* num_resources */ XtNumber(resources), 171 /* xrm_class */ NULLQUARK, 172 /* compress_motion */ TRUE, 173 /* compress_exposure */ XtExposeCompressMaximal| 174 XtExposeGraphicsExposeMerged, 175 /* compress_enterleave */ TRUE, 176 /* visible_interest */ TRUE, 177 /* destroy */ Destroy, 178 /* resize */ Resize, 179 /* expose */ Redisplay, 180 /* set_values */ SetValues, 181 /* set_values_hook */ NULL, 182 /* set_values_almost */ XtInheritSetValuesAlmost, 183 /* get_values_hook */ NULL, 184 /* accept_focus */ NULL, 185 /* version */ XtVersion, 186 /* callback_private */ NULL, 187 /* tm_table */ translations, 188 /* query_geometry */ XtInheritQueryGeometry, 189 /* display_accelerator */ XtInheritDisplayAccelerator, 190 /* extension */ NULL 191 }, 192 { 193 /* change_sensitive */ XtInheritChangeSensitive, 194 } 195 }; 196 197 WidgetClass scaleWidgetClass = (WidgetClass) &scaleClassRec; 198 199 200 202 /* 203 * Private Procedures 204 */ 205 206 207 static void 208 ClassInitialize(void) 209 { 210 } 211 212 213 215 static void 216 GetGC(ScaleWidget sw) 217 { 218 XGCValues values; 219 220 values.foreground = sw->scale.foreground_pixel; 221 values.background = sw->core.background_pixel; 222 values.function = GXcopy; 223 224 sw->scale.gc = XtGetGC((Widget) sw, 225 GCForeground | 226 GCBackground | 227 GCFunction, 228 &values); 229 } 230 231 232 234 235 static void 236 GetInitialScaleValues(ScaleWidget sw) 237 { 238 if (sw->scale.proportional) { 239 sw->scale.scale_x = sw->scale.scale_y = 240 ((sw->scale.aspect_ratio > 1.0) ? 241 sw->scale.aspect_ratio : 1.0 / sw->scale.aspect_ratio) * 242 (sw->scale.precision > 1.0 ? 243 sw->scale.precision : 1.0); 244 Proportional(sw); /* need to cut them down to proper values */ 245 } 246 else 247 sw->scale.scale_x = sw->scale.scale_y = 1.0; 248 } 249 250 251 253 static void 254 GetRectangleBuffer(ScaleWidget sw, Cardinal buffer_size) 255 /* 256 * This procedure will realloc a new rectangles buffer. 257 * If the new buffer size is less than nrectangles, some 258 * information will be lost. 259 */ 260 { 261 if (buffer_size == 0) { 262 buffer_size = DefaultBufferSize; 263 XtWarning("buffer size has to be a positive number greater than zero"); 264 } 265 sw->scale.rectangles = (XRectangle *) 266 XtRealloc((char *) sw->scale.rectangles, 267 buffer_size * sizeof(XRectangle)); 268 sw->scale.buffer_size = buffer_size; 269 } 270 271 272 274 static void 275 Initialize(_X_UNUSED Widget request, Widget new, 276 _X_UNUSED ArgList args, _X_UNUSED Cardinal *num_args) 277 { 278 ScaleWidget new_sw = (ScaleWidget) new; 279 280 new_sw->scale.table.x = (Position *) NULL; 281 new_sw->scale.table.y = (Position *) NULL; 282 new_sw->scale.table.width = (Dimension *) NULL; 283 new_sw->scale.table.height = (Dimension *) NULL; 284 285 new_sw->scale.nrectangles = 0; 286 new_sw->scale.rectangles = (XRectangle *) NULL; 287 288 GetRectangleBuffer(new_sw, new_sw->scale.buffer_size); 289 290 GetGC(new_sw); 291 292 if (new_sw->scale.image != NULL) { 293 if (new_sw->core.width == 0) 294 new_sw->core.width = 295 new_sw->scale.image->width + 2 * new_sw->scale.internal_width; 296 if (new_sw->core.height == 0) 297 new_sw->core.height = 298 new_sw->scale.image->height + 2 *new_sw->scale.internal_height; 299 } 300 else { 301 if (new_sw->core.width == 0) 302 new_sw->core.width = 1 + 2 * new_sw->scale.internal_width; 303 if (new_sw->core.height == 0) 304 new_sw->core.height = 1 + 2 * new_sw->scale.internal_height; 305 new_sw->scale.image = XCreateImage(XtDisplay(new), 306 DefaultVisual(XtDisplay(new), 307 DefaultScreen(XtDisplay(new))), 308 1, XYBitmap, 0, 309 XtCalloc(1, sizeof(char)), 310 1, 1, 8, 0); 311 } 312 313 if ((new_sw->scale.aspect_ratio = 314 atof(new_sw->scale.aspect_ratio_str)) < 0.0) { 315 new_sw->scale.aspect_ratio = 1.0; 316 XtWarning("AspectRatio has to be a positive number. (forced to 1.0)"); 317 } 318 319 if ((new_sw->scale.precision = 320 atof(new_sw->scale.precision_str)) < 0.0) { 321 new_sw->scale.precision = 0.001; 322 XtWarning("Precision has to be a positive number. (forced to 0.001)"); 323 } 324 325 if (new_sw->scale.scale_x_str == DefaultScaleFactor 326 || 327 new_sw->scale.scale_y_str == DefaultScaleFactor) 328 GetInitialScaleValues(new_sw); 329 else { 330 if ((new_sw->scale.scale_x = 331 atof(new_sw->scale.scale_x_str)) < 0.0) { 332 new_sw->scale.scale_x = 1.0; 333 XtWarning("ScaleValue has to be a positive number. (forced to 1.0)"); 334 } 335 if ((new_sw->scale.scale_y = 336 atof(new_sw->scale.scale_y_str)) < 0.0) { 337 new_sw->scale.scale_y = 1.0; 338 XtWarning("ScaleValue has to be a positive number. (forced to 1.0)"); 339 } 340 } 341 } 342 343 344 346 static void 347 BuildTable(ScaleWidget sw) 348 /* 349 * This procedure builds scaling table for image in the scale struct 350 * Requires image, scale_x and scale_y to be set properly 351 */ 352 { 353 Position x, y; 354 355 XtFree((char *) sw->scale.table.x); 356 XtFree((char *) sw->scale.table.y); 357 XtFree((char *) sw->scale.table.width); 358 XtFree((char *) sw->scale.table.height); 359 sw->scale.table.x = 360 (Position *) XtMalloc(sizeof(Position) * sw->scale.image->width); 361 sw->scale.table.y = 362 (Position *) XtMalloc(sizeof(Position) * sw->scale.image->height); 363 sw->scale.table.width = 364 (Dimension *) XtMalloc(sizeof(Dimension) * sw->scale.image->width); 365 sw->scale.table.height = 366 (Dimension *) XtMalloc(sizeof(Dimension) * sw->scale.image->height); 367 368 /* Build the scaling table */ 369 for (x = 0; x < sw->scale.image->width; x++) { 370 sw->scale.table.x[(int) x] = (Position) myrint(sw->scale.scale_x * x); 371 sw->scale.table.width[(int) x] = (Dimension) 372 myrint(sw->scale.scale_x *(x + 1)) - myrint(sw->scale.scale_x * x); 373 } 374 for (y = 0; y < sw->scale.image->height; y++) { 375 sw->scale.table.y[(int) y] = (Position) myrint(sw->scale.scale_y * y); 376 sw->scale.table.height[(int) y] = (Dimension) 377 myrint(sw->scale.scale_y *(y + 1)) - myrint(sw->scale.scale_y * y); 378 } 379 } 380 381 382 384 static void 385 FlushRectangles(ScaleWidget sw, Drawable drawable, GC gc) 386 { 387 XFillRectangles(XtDisplay(sw), drawable, gc, 388 sw->scale.rectangles, sw->scale.nrectangles); 389 390 sw->scale.nrectangles = 0; 391 } 392 393 394 396 static void 397 FillRectangle(ScaleWidget sw, Drawable drawable, GC gc, 398 Position x, Position y, Dimension width, Dimension height) 399 { 400 401 if (sw->scale.nrectangles == sw->scale.buffer_size) 402 FlushRectangles(sw, drawable, gc); 403 404 sw->scale.rectangles[(int) sw->scale.nrectangles].x = x; 405 sw->scale.rectangles[(int) sw->scale.nrectangles].y = y; 406 sw->scale.rectangles[(int) sw->scale.nrectangles].width = width; 407 sw->scale.rectangles[(int) sw->scale.nrectangles].height = height; 408 409 ++sw->scale.nrectangles; 410 } 411 412 413 415 static void 416 ScaleImage(ScaleWidget sw, Drawable drawable, Position img_x, Position img_y, 417 Position dst_x, Position dst_y, 418 Dimension img_width, Dimension img_height) 419 /* 420 * This procedure scales image into the specified drawable 421 * It assumes scaling table was already built 422 */ 423 { 424 GC gc; 425 XGCValues gcv; 426 Position x, y; 427 Pixel pixel; 428 429 /* Clip the img coordinates */ 430 img_x = min(max(img_x, 0), (Position) sw->scale.image->width - 1); 431 img_y = min(max(img_y, 0), (Position) sw->scale.image->height - 1); 432 img_width = 433 min(img_width, (Dimension)(sw->scale.image->width - (Dimension)img_x)); 434 img_height = 435 min(img_height, (Dimension)(sw->scale.image->height - (Dimension)img_y)); 436 437 if (sw->scale.scale_x == 1.0 && sw->scale.scale_y == 1.0) 438 XPutImage(XtDisplay(sw), drawable, sw->scale.gc, sw->scale.image, 439 img_x, img_y, dst_x, dst_y, 440 img_width, img_height); 441 else { 442 dst_x = dst_x - sw->scale.table.x[(int) img_x]; 443 dst_y = dst_y - sw->scale.table.y[(int) img_y]; 444 445 gc = XCreateGC(XtDisplay(sw), drawable, 0, NULL); 446 447 gcv.function = GXcopy; 448 XChangeGC(XtDisplay(sw), gc, GCFunction, &gcv); 449 450 /* make sure gc knows the right background */ 451 gcv.background = sw->core.background_pixel; 452 XChangeGC(XtDisplay(sw), gc, GCBackground, &gcv); 453 454 /* Set the background of drawable. If the most frequent color 455 is the background color it can speed up scaling process. */ 456 gcv.foreground = gcv.background; 457 XChangeGC(XtDisplay(sw), gc, GCForeground, &gcv); 458 XFillRectangle(XtDisplay(sw), drawable, gc, 459 sw->scale.table.x[(int) img_x] + dst_x, 460 sw->scale.table.y[(int) img_y] + dst_y, 461 sw->scale.table.x[(int) img_x + img_width - 1] - 462 sw->scale.table.x[(int) img_x], 463 sw->scale.table.y[(int) img_y + img_height - 1] - 464 sw->scale.table.y[(int) img_y]); 465 466 if (sw->scale.image->format == XYBitmap) { 467 for (x = img_x; x < img_x + (Position)img_width; x++) 468 for (y = img_y; y < img_y + (Position)img_height; y++) { 469 pixel = XGetPixel(sw->scale.image, x, y); 470 if (pixel) /* Do not draw background */ 471 FillRectangle(sw, drawable, sw->scale.gc, 472 sw->scale.table.x[(int) x] + dst_x, 473 sw->scale.table.y[(int) y] + dst_y, 474 sw->scale.table.width[(int) x], 475 sw->scale.table.height[(int) y]); 476 } 477 FlushRectangles(sw, drawable, sw->scale.gc); 478 } 479 else { 480 for (x = img_x; x < img_x + (Position)img_width; x++) 481 for (y = img_y; y < img_y + (Position)img_height; y++) { 482 pixel = XGetPixel(sw->scale.image, x, y); 483 if (pixel != gcv.background) { /* Do not draw background */ 484 if (gcv.foreground != pixel) { /* Change fg to pixel */ 485 gcv.foreground = pixel; 486 XChangeGC(XtDisplay(sw), gc, GCForeground, &gcv); 487 } 488 XFillRectangle(XtDisplay(sw), drawable, gc, 489 sw->scale.table.x[(int) x] + dst_x, 490 sw->scale.table.y[(int) y] + dst_y, 491 sw->scale.table.width[(int) x], 492 sw->scale.table.height[(int) y]); 493 } 494 } 495 } 496 XFreeGC(XtDisplay(sw), gc); 497 } 498 } 499 500 501 503 static int 504 FindPixel(ScaleWidget sw, Position x, Position y, 505 Position *img_x, Position *img_y, Pixel *img_pixel) 506 /* (x,y) == (0,0) where image starts in sw window*/ 507 { 508 if (*img_x < 0 || *img_x >= sw->scale.image->width 509 || 510 *img_y < 0 || *img_y >= sw->scale.image->height) 511 return (-1); 512 513 if (sw->scale.table.x[(int) *img_x] >= x) { 514 --*img_x; 515 return FindPixel(sw, x, y, img_x, img_y, img_pixel); 516 } 517 if (sw->scale.table.x[(int) *img_x] + 518 (Position)sw->scale.table.width[(int) *img_x] < x) { 519 ++*img_x; 520 return FindPixel(sw, x, y, img_x, img_y, img_pixel); 521 } 522 if (sw->scale.table.y[(int) *img_y] >= y) { 523 --*img_y; 524 return FindPixel(sw, x, y, img_x, img_y, img_pixel); 525 } 526 if (sw->scale.table.y[(int) *img_y] + 527 (Position)sw->scale.table.height[(int) *img_y] < y) { 528 ++*img_y; 529 return FindPixel(sw, x, y, img_x, img_y, img_pixel); 530 } 531 532 *img_pixel = XGetPixel(sw->scale.image, *img_x, *img_y); 533 534 return (0); 535 } 536 537 538 540 int 541 SWGetImagePixel(Widget w, Position x, Position y, 542 Position *img_x, Position *img_y, Pixel *img_pixel) 543 { 544 ScaleWidget sw = (ScaleWidget) w; 545 546 x -= sw->scale.x; 547 y -= sw->scale.y; 548 549 *img_x = (Position) floor(x / sw->scale.scale_x); 550 *img_y = (Position) floor(y / sw->scale.scale_y); 551 552 return FindPixel(sw, x, y, img_x, img_y, img_pixel); 553 } 554 555 556 558 static void 559 Redisplay(Widget w, XEvent *event, _X_UNUSED Region region) 560 { 561 ScaleWidget sw = (ScaleWidget) w; 562 Position x, y, img_x, img_y; 563 Dimension width, height; 564 565 if (event->type == Expose) { 566 567 if (event->xexpose.x < sw->scale.x) { 568 x = 0; 569 width = event->xexpose.width - 570 (sw->scale.x - event->xexpose.x); 571 } 572 else { 573 x = event->xexpose.x - sw->scale.x; 574 width = event->xexpose.width; 575 } 576 577 if (event->xexpose.y < sw->scale.y) { 578 y = 0; 579 height = event->xexpose.height - 580 (sw->scale.y - event->xexpose.y); 581 } 582 else { 583 y = event->xexpose.y - sw->scale.y; 584 height = event->xexpose.height; 585 } 586 587 img_x = min(max((Position) floor(x / sw->scale.scale_x), 0), 588 (Position) sw->scale.image->width - 1); 589 590 img_y = min(max((Position) floor(y / sw->scale.scale_y), 0), 591 (Position) sw->scale.image->height - 1); 592 593 if (sw->core.visible) { 594 ScaleImage(sw, XtWindow(w), 595 img_x, img_y, 596 sw->scale.x + sw->scale.table.x[(int) img_x], 597 sw->scale.y + sw->scale.table.y[(int) img_y], 598 (Dimension) ceil(width 599 / sw->scale.scale_x) + 1, 600 (Dimension) ceil(height 601 / sw->scale.scale_y) + 1); 602 } 603 } 604 } 605 606 607 609 static void 610 TryResize(ScaleWidget sw) 611 { 612 Dimension width, height; 613 XtGeometryResult result; 614 615 width = (Dimension) 616 floor(sw->scale.image->width * sw->scale.scale_x) 617 + 2 * sw->scale.internal_width; 618 height = (Dimension) 619 floor(sw->scale.image->height * sw->scale.scale_y) 620 + 2 * sw->scale.internal_height; 621 622 while ((result = 623 /* SUPPRESS 530 */XtMakeResizeRequest((Widget)sw,width,height,&width,&height)) 624 == XtGeometryAlmost); 625 626 if (result != XtGeometryNo) { 627 sw->core.width = width; 628 sw->core.height = height; 629 } 630 } 631 632 633 635 static void 636 Precision(ScaleWidget sw) 637 { 638 if (sw->scale.scale_x != 1.0) 639 sw->scale.scale_x = floor(sw->scale.scale_x / sw->scale.precision) 640 * sw->scale.precision; 641 642 if (sw->scale.scale_y != 1.0) 643 sw->scale.scale_y = floor(sw->scale.scale_y / sw->scale.precision) 644 * sw->scale.precision; 645 } 646 647 648 650 static void 651 Proportional(ScaleWidget sw) 652 { 653 double scale_x, scale_y; 654 655 scale_x = sw->scale.scale_y / sw->scale.aspect_ratio; 656 scale_y = sw->scale.scale_x * sw->scale.aspect_ratio; 657 658 if (scale_x <= sw->scale.scale_x && scale_y <= sw->scale.scale_y) { 659 if (scale_x > scale_y) 660 sw->scale.scale_x = scale_x; 661 else 662 sw->scale.scale_y = scale_y; 663 } 664 else if (scale_x <= sw->scale.scale_x) 665 sw->scale.scale_x = scale_x; 666 else if (scale_y <= sw->scale.scale_y) 667 sw->scale.scale_y = scale_y; 668 else { 669 double x_ratio, y_ratio; 670 671 x_ratio = scale_x / sw->scale.scale_x; 672 y_ratio = scale_y / sw->scale.scale_y; 673 674 if (x_ratio < y_ratio) 675 sw->scale.scale_y /= x_ratio; 676 else 677 sw->scale.scale_x /= y_ratio; 678 } 679 680 if (fabs(sw->scale.scale_x / sw->scale.scale_y * sw->scale.aspect_ratio 681 - 1.0) > sw->scale.precision) 682 XtWarning("can not preserve aspect ratio"); 683 } 684 685 686 688 static void 689 GetScaledSize(ScaleWidget sw) 690 { 691 sw->scale.width = (Dimension) 692 max(myrint(sw->scale.scale_x * sw->scale.image->width), 1); 693 sw->scale.height = (Dimension) 694 max(myrint(sw->scale.scale_y * sw->scale.image->height), 1); 695 } 696 697 698 700 static void 701 GetScaleValues(ScaleWidget sw) 702 { 703 /* 704 * Make sure to subtract internal width and height. 705 */ 706 707 sw->scale.scale_x = 708 max((int)(sw->core.width - 2 * sw->scale.internal_width), 1) 709 / sw->scale.image->width; 710 711 sw->scale.scale_y = 712 max((int)(sw->core.height - 2 * sw->scale.internal_height), 1) 713 / sw->scale.image->height; 714 } 715 716 717 719 static void 720 Unscale(ScaleWidget sw) 721 { 722 sw->scale.scale_x = sw->scale.scale_y = 1.0; 723 724 GetScaledSize(sw); 725 726 BuildTable(sw); 727 } 728 729 730 732 static void 733 Autoscale(ScaleWidget sw) 734 { 735 GetScaleValues(sw); 736 737 if (sw->scale.proportional) Proportional(sw); 738 739 Precision(sw); 740 741 GetScaledSize(sw); 742 743 BuildTable(sw); 744 } 745 746 747 749 static void 750 PositionImage(ScaleWidget sw) 751 { 752 /* 753 * Set as if for ForgegGravity (that is center the image) 754 */ 755 sw->scale.x = (Position) 756 (sw->core.width - sw->scale.width) / 2; 757 sw->scale.y = (Position) 758 (sw->core.height - sw->scale.height) / 2; 759 760 /***** 761 if (sw->scale.gravity & WestGravity) { 762 } 763 if (sw->scale.gravity & EastGravity) { 764 } 765 if (sw->scale.gravity & NorthGravity) { 766 } 767 if (sw->scale.gravity & SouthGravity) { 768 } 769 *****/ 770 } 771 772 773 775 static void 776 Resize(Widget w) 777 { 778 ScaleWidget sw = (ScaleWidget) w; 779 780 if (sw->scale.autoscale) Autoscale(sw); 781 782 PositionImage(sw); 783 } 784 785 786 788 static void 789 Realize(Widget wid, Mask *vmask, XSetWindowAttributes *attr) 790 { 791 ScaleWidget sw = (ScaleWidget) wid; 792 XtCreateWindow(wid, (unsigned int) InputOutput, 793 sw->scale.visual, *vmask, attr); 794 } 795 796 797 799 static void 800 Destroy(Widget w) 801 { 802 ScaleWidget sw = (ScaleWidget) w; 803 804 XtFree((char *) sw->scale.table.x); 805 XtFree((char *) sw->scale.table.y); 806 XtFree((char *) sw->scale.table.width); 807 XtFree((char *) sw->scale.table.height); 808 809 XtFree((char *) sw->scale.rectangles); 810 811 XtReleaseGC(w, sw->scale.gc); 812 813 XDestroyImage(sw->scale.image); 814 } 815 816 817 819 static Boolean 820 SetValues(Widget current, _X_UNUSED Widget request, Widget new, 821 ArgList args, Cardinal *num_args) 822 { 823 ScaleWidget cur_sw = (ScaleWidget) current; 824 /* ScaleWidget req_sw = (ScaleWidget) request; */ 825 ScaleWidget new_sw = (ScaleWidget) new; 826 Boolean redisplay = False; 827 Cardinal i; 828 829 for (i = 0; i < *num_args; i++) { 830 if (streq(XtNbackground, args[i].name)) { 831 XSetBackground(XtDisplay(new), new_sw->scale.gc, 832 new_sw->core.background_pixel); 833 } 834 if (streq(XtNforeground, args[i].name)) { 835 XSetForeground(XtDisplay(new), new_sw->scale.gc, 836 new_sw->scale.foreground_pixel); 837 } 838 if (streq(XtNimage, args[i].name)) { 839 XDestroyImage(cur_sw->scale.image); 840 if (new_sw->scale.image == NULL) 841 new_sw->scale.image = XCreateImage(XtDisplay(new), 842 DefaultVisual(XtDisplay(new), 843 DefaultScreen(XtDisplay(new))), 844 1, XYBitmap, 0, 845 XtCalloc(1, sizeof(char)), 846 1, 1, 8, 0); 847 else 848 new_sw->scale.image = 849 XSubImage(new_sw->scale.image, 850 0, 0, 851 new_sw->scale.image->width, 852 new_sw->scale.image->height); 853 854 if (new_sw->scale.resize) 855 TryResize(new_sw); 856 if (new_sw->scale.autoscale) 857 Autoscale(new_sw); 858 else { 859 GetScaledSize(new_sw); 860 BuildTable(new_sw); 861 } 862 PositionImage(new_sw); 863 redisplay = True; 864 } 865 866 if (streq(XtNuserData, args[i].name)) 867 new_sw->scale.userData = (XtPointer)args[i].value; 868 869 if (streq(XtNbufferSize, args[i].name)) { 870 if (new_sw->scale.buffer_size != cur_sw->scale.buffer_size) { 871 GetRectangleBuffer(new_sw, new_sw->scale.buffer_size); 872 } 873 } 874 875 if (streq(XtNaspectRatio, args[i].name)) { 876 if ((new_sw->scale.aspect_ratio = 877 atof(new_sw->scale.aspect_ratio_str)) < 0.0) { 878 new_sw->scale.aspect_ratio = cur_sw->scale.aspect_ratio; 879 XtWarning("AspectRatio has to be a positive number."); 880 } 881 else if (new_sw->scale.aspect_ratio != cur_sw->scale.aspect_ratio){ 882 if (new_sw->scale.proportional) { 883 Proportional(new_sw); 884 Precision(new_sw); 885 GetScaledSize(new_sw); 886 BuildTable(new_sw); 887 PositionImage(new_sw); 888 redisplay = True; 889 } 890 } 891 } 892 893 if (streq(XtNproportional, args[i].name)) { 894 if (new_sw->scale.proportional != cur_sw->scale.proportional) { 895 if (new_sw->scale.proportional) Proportional(new_sw); 896 Precision(new_sw); 897 GetScaledSize(new_sw); 898 BuildTable(new_sw); 899 PositionImage(new_sw); 900 redisplay = True; 901 } 902 } 903 904 if (streq(XtNscaleX, args[i].name) 905 || 906 streq(XtNscaleY, args[i].name)) { 907 if (new_sw->scale.scale_x_str == DefaultScaleFactor 908 || 909 new_sw->scale.scale_y_str == DefaultScaleFactor) 910 GetInitialScaleValues(new_sw); 911 else { 912 if ((new_sw->scale.scale_x = 913 atof(new_sw->scale.scale_x_str)) < 0.0) { 914 new_sw->scale.scale_x = cur_sw->scale.scale_x; 915 XtWarning("ScaleValue has to be a positive number."); 916 } 917 if ((new_sw->scale.scale_y = 918 atof(new_sw->scale.scale_y_str)) < 0.0) { 919 new_sw->scale.scale_y = cur_sw->scale.scale_y; 920 XtWarning("ScaleValue has to be a positive number."); 921 } 922 } 923 if (new_sw->scale.scale_x != cur_sw->scale.scale_x 924 || 925 new_sw->scale.scale_y != cur_sw->scale.scale_y) { 926 927 /*?*?*?*?*?*?*?*?*?*?*?*?*?*?**?*?*?*?*?*?*?*?*?***?*/ 928 fprintf(stderr, "================>>%f %f\n", 929 new_sw->scale.scale_x, new_sw->scale.scale_y); 930 931 if (new_sw->scale.proportional) Proportional(new_sw); 932 Precision(new_sw); 933 if (new_sw->scale.resize) 934 TryResize(new_sw); 935 GetScaledSize(new_sw); 936 BuildTable(new_sw); 937 PositionImage(new_sw); 938 redisplay = True; 939 } 940 } 941 942 if (streq(XtNprecision, args[i].name)) { 943 if ((new_sw->scale.precision = 944 atof(new_sw->scale.precision_str)) < 0.0) { 945 new_sw->scale.precision = cur_sw->scale.precision; 946 XtWarning("Precision has to be a positive number."); 947 } 948 if (new_sw->scale.precision != cur_sw->scale.precision) { 949 if (new_sw->scale.proportional) Proportional(new_sw); 950 Precision(new_sw); 951 GetScaledSize(new_sw); 952 BuildTable(new_sw); 953 PositionImage(new_sw); 954 redisplay = True; 955 } 956 } 957 } 958 return(redisplay); 959 } 960 961 962 964 void 965 SWUnscale(Widget w, _X_UNUSED XEvent *event, 966 _X_UNUSED String *params, _X_UNUSED Cardinal *num_params) 967 { 968 ScaleWidget sw = (ScaleWidget) w; 969 970 Unscale(sw); 971 PositionImage(sw); 972 XClearArea(XtDisplay(w), XtWindow(w), 0, 0, 0, 0, True); 973 } 974 975 976 978 void 979 SWAutoscale(Widget w, _X_UNUSED XEvent *event, 980 _X_UNUSED String *params, _X_UNUSED Cardinal *num_params) 981 { 982 ScaleWidget sw = (ScaleWidget) w; 983 984 Autoscale(sw); 985 PositionImage(sw); 986 XClearArea(XtDisplay(w), XtWindow(w), 0, 0, 0, 0, True); 987 } 988 989 990 992 void 993 SWInitialSize(Widget w, _X_UNUSED XEvent *event, 994 _X_UNUSED String *params, _X_UNUSED Cardinal *num_params) 995 { 996 ScaleWidget sw = (ScaleWidget) w; 997 998 GetInitialScaleValues(sw); 999 1000 if (sw->scale.proportional) Proportional(sw); 1001 Precision(sw); 1002 if (sw->scale.resize) 1003 TryResize(sw); 1004 GetScaledSize(sw); 1005 BuildTable(sw); 1006 PositionImage(sw); 1007 XClearArea(XtDisplay(w), XtWindow(w), 0, 0, 0, 0, True); 1008 } 1009 1010 1011 1013 void 1014 SWSetImage(Widget w, XImage *image) 1015 { 1016 int n; 1017 Arg wargs[2]; 1018 1019 n = 0; 1020 XtSetArg(wargs[n], XtNimage, (XtArgVal) image); n++; 1021 XtSetValues(w, wargs, n); 1022 } 1023 1024 1025 1027 1028 void 1029 RequestSelection(Widget w, XEvent *event, 1030 _X_UNUSED String *params, _X_UNUSED Cardinal *num_params) 1031 { 1032 SWRequestSelection(w, event->xbutton.time); 1033 } 1034 1035 1036 1038 void 1039 GrabSelection(Widget w, XEvent *event, 1040 _X_UNUSED String *params, _X_UNUSED Cardinal *num_params) 1041 { 1042 SWGrabSelection(w, event->xbutton.time); 1043 } 1044 1045 1046 1048 Pixmap 1049 SWGetPixmap(Widget w) 1050 { 1051 ScaleWidget sw = (ScaleWidget) w; 1052 Pixmap pixmap; 1053 1054 pixmap = XCreatePixmap(XtDisplay(w), XtWindow(w), 1055 sw->scale.width, 1056 sw->scale.height, 1057 sw->scale.image->depth); 1058 ScaleImage(sw, pixmap, 1059 0, 0, 0, 0, 1060 (Dimension) sw->scale.image->width, 1061 (Dimension) sw->scale.image->height); 1062 1063 return(pixmap); 1064 } 1065