History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/awt/image/GifImageDecoder.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/awt/image/GifImageDecoder.java+753−0
@@ -0,0 +1,753 @@
1+/*
2+ * Copyright (c) 1995, 2006, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+/*-
27+ * Reads GIF images from an InputStream and reports the
28+ * image data to an InputStreamImageSource object.
29+ *
30+ * The algorithm is copyright of CompuServe.
31+ */
32+package sun.awt.image;
33+
34+import java.util.Vector;
35+import java.util.Hashtable;
36+import java.io.InputStream;
37+import java.io.IOException;
38+import java.awt.image.*;
39+
40+/**
41+ * Gif Image converter
42+ *
43+ * @author Arthur van Hoff
44+ * @author Jim Graham
45+ */
46+public class GifImageDecoder extends ImageDecoder {
47+ private static final boolean verbose = false;
48+
49+ private static final int IMAGESEP = 0x2c;
50+ private static final int EXBLOCK = 0x21;
51+ private static final int EX_GRAPHICS_CONTROL= 0xf9;
52+ private static final int EX_COMMENT = 0xfe;
53+ private static final int EX_APPLICATION = 0xff;
54+ private static final int TERMINATOR = 0x3b;
55+ private static final int TRANSPARENCYMASK = 0x01;
56+ private static final int INTERLACEMASK = 0x40;
57+ private static final int COLORMAPMASK = 0x80;
58+
59+ int num_global_colors;
60+ byte[] global_colormap;
61+ int trans_pixel = -1;
62+ IndexColorModel global_model;
63+
64+ Hashtable props = new Hashtable();
65+
66+ byte[] saved_image;
67+ IndexColorModel saved_model;
68+
69+ int global_width;
70+ int global_height;
71+ int global_bgpixel;
72+
73+ GifFrame curframe;
74+
75+ public GifImageDecoder(InputStreamImageSource src, InputStream is) {
76+ super(src, is);
77+ }
78+
79+ /**
80+ * An error has occurred. Throw an exception.
81+ */
82+ private static void error(String s1) throws ImageFormatException {
83+ throw new ImageFormatException(s1);
84+ }
85+
86+ /**
87+ * Read a number of bytes into a buffer.
88+ * @return number of bytes that were not read due to EOF or error
89+ */
90+ private int readBytes(byte buf[], int off, int len) {
91+ while (len > 0) {
92+ try {
93+ int n = input.read(buf, off, len);
94+ if (n < 0) {
95+ break;
96+ }
97+ off += n;
98+ len -= n;
99+ } catch (IOException e) {
100+ break;
101+ }
102+ }
103+ return len;
104+ }
105+
106+ private static final int ExtractByte(byte buf[], int off) {
107+ return (buf[off] & 0xFF);
108+ }
109+
110+ private static final int ExtractWord(byte buf[], int off) {
111+ return (buf[off] & 0xFF) | ((buf[off + 1] & 0xFF) << 8);
112+ }
113+
114+ /**
115+ * produce an image from the stream.
116+ */
117+ public void produceImage() throws IOException, ImageFormatException {
118+ try {
119+ readHeader();
120+
121+ int totalframes = 0;
122+ int frameno = 0;
123+ int nloops = -1;
124+ int disposal_method = 0;
125+ int delay = -1;
126+ boolean loopsRead = false;
127+ boolean isAnimation = false;
128+
129+ while (!aborted) {
130+ int code;
131+
132+ switch (code = input.read()) {
133+ case EXBLOCK:
134+ switch (code = input.read()) {
135+ case EX_GRAPHICS_CONTROL: {
136+ byte buf[] = new byte[6];
137+ if (readBytes(buf, 0, 6) != 0) {
138+ return;//error("corrupt GIF file");
139+ }
140+ if ((buf[0] != 4) || (buf[5] != 0)) {
141+ return;//error("corrupt GIF file (GCE size)");
142+ }
143+ // Get the index of the transparent color
144+ delay = ExtractWord(buf, 2) * 10;
145+ if (delay > 0 && !isAnimation) {
146+ isAnimation = true;
147+ ImageFetcher.startingAnimation();
148+ }
149+ disposal_method = (buf[1] >> 2) & 7;
150+ if ((buf[1] & TRANSPARENCYMASK) != 0) {
151+ trans_pixel = ExtractByte(buf, 4);
152+ } else {
153+ trans_pixel = -1;
154+ }
155+ break;
156+ }
157+
158+ case EX_COMMENT:
159+ case EX_APPLICATION:
160+ default:
161+ boolean loop_tag = false;
162+ String comment = "";
163+ while (true) {
164+ int n = input.read();
165+ if (n <= 0) {
166+ break;
167+ }
168+ byte buf[] = new byte[n];
169+ if (readBytes(buf, 0, n) != 0) {
170+ return;//error("corrupt GIF file");
171+ }
172+ if (code == EX_COMMENT) {
173+ comment += new String(buf, 0);
174+ } else if (code == EX_APPLICATION) {
175+ if (loop_tag) {
176+ if (n == 3 && buf[0] == 1) {
177+ if (loopsRead) {
178+ ExtractWord(buf, 1);
179+ }
180+ else {
181+ nloops = ExtractWord(buf, 1);
182+ loopsRead = true;
183+ }
184+ } else {
185+ loop_tag = false;
186+ }
187+ }
188+ if ("NETSCAPE2.0".equals(new String(buf, 0))) {
189+ loop_tag = true;
190+ }
191+ }
192+ }
193+ if (code == EX_COMMENT) {
194+ props.put("comment", comment);
195+ }
196+ if (loop_tag && !isAnimation) {
197+ isAnimation = true;
198+ ImageFetcher.startingAnimation();
199+ }
200+ break;
201+
202+ case -1:
203+ return; //error("corrupt GIF file");
204+ }
205+ break;
206+
207+ case IMAGESEP:
208+ if (!isAnimation) {
209+ input.mark(0); // we don't need the mark buffer
210+ }
211+ try {
212+ if (!readImage(totalframes == 0,
213+ disposal_method,
214+ delay)) {
215+ return;
216+ }
217+ } catch (Exception e) {
218+ if (verbose) {
219+ e.printStackTrace();
220+ }
221+ return;
222+ }
223+ frameno++;
224+ totalframes++;
225+ break;
226+
227+ default:
228+ case -1:
229+ if (verbose) {
230+ if (code == -1) {
231+ System.err.println("Premature EOF in GIF file," +
232+ " frame " + frameno);
233+ } else {
234+ System.err.println("corrupt GIF file (parse) ["
235+ + code + "].");
236+ }
237+ }
238+ if (frameno == 0) {
239+ return;
240+ }
241+ // NOBREAK
242+
243+ case TERMINATOR:
244+ if (nloops == 0 || nloops-- >= 0) {
245+ try {
246+ if (curframe != null) {
247+ curframe.dispose();
248+ curframe = null;
249+ }
250+ input.reset();
251+ saved_image = null;
252+ saved_model = null;
253+ frameno = 0;
254+ break;
255+ } catch (IOException e) {
256+ return; // Unable to reset input buffer
257+ }
258+ }
259+ if (verbose && frameno != 1) {
260+ System.out.println("processing GIF terminator,"
261+ + " frames: " + frameno
262+ + " total: " + totalframes);
263+ }
264+ imageComplete(ImageConsumer.STATICIMAGEDONE, true);
265+ return;
266+ }
267+ }
268+ } finally {
269+ close();
270+ }
271+ }
272+
273+ /**
274+ * Read Image header
275+ */
276+ private void readHeader() throws IOException, ImageFormatException {
277+ // Create a buffer
278+ byte buf[] = new byte[13];
279+
280+ // Read the header
281+ if (readBytes(buf, 0, 13) != 0) {
282+ throw new IOException();
283+ }
284+
285+ // Check header
286+ if ((buf[0] != 'G') || (buf[1] != 'I') || (buf[2] != 'F')) {
287+ error("not a GIF file.");
288+ }
289+
290+ // Global width&height
291+ global_width = ExtractWord(buf, 6);
292+ global_height = ExtractWord(buf, 8);
293+
294+ // colormap info
295+ int ch = ExtractByte(buf, 10);
296+ if ((ch & COLORMAPMASK) == 0) {
297+ // no global colormap so make up our own
298+ // If there is a local colormap, it will override what we
299+ // have here. If there is not a local colormap, the rules
300+ // for GIF89 say that we can use whatever colormap we want.
301+ // This means that we should probably put in a full 256 colormap
302+ // at some point. REMIND!
303+ num_global_colors = 2;
304+ global_bgpixel = 0;
305+ global_colormap = new byte[2*3];
306+ global_colormap[0] = global_colormap[1] = global_colormap[2] = (byte)0;
307+ global_colormap[3] = global_colormap[4] = global_colormap[5] = (byte)255;
308+
309+ }
310+ else {
311+ num_global_colors = 1 << ((ch & 0x7) + 1);
312+
313+ global_bgpixel = ExtractByte(buf, 11);
314+
315+ if (buf[12] != 0) {
316+ props.put("aspectratio", ""+((ExtractByte(buf, 12) + 15) / 64.0));
317+ }
318+
319+ // Read colors
320+ global_colormap = new byte[num_global_colors * 3];
321+ if (readBytes(global_colormap, 0, num_global_colors * 3) != 0) {
322+ throw new IOException();
323+ }
324+ }
325+ input.mark(Integer.MAX_VALUE); // set this mark in case this is an animated GIF
326+ }
327+
328+ /**
329+ * The ImageConsumer hints flag for a non-interlaced GIF image.
330+ */
331+ private static final int normalflags =
332+ ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES |
333+ ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME;
334+
335+ /**
336+ * The ImageConsumer hints flag for an interlaced GIF image.
337+ */
338+ private static final int interlaceflags =
339+ ImageConsumer.RANDOMPIXELORDER | ImageConsumer.COMPLETESCANLINES |
340+ ImageConsumer.SINGLEPASS | ImageConsumer.SINGLEFRAME;
341+
342+ private short prefix[] = new short[4096];
343+ private byte suffix[] = new byte[4096];
344+ private byte outCode[] = new byte[4097];
345+
346+ private static native void initIDs();
347+
348+ static {
349+ /* ensure that the necessary native libraries are loaded */
350+ NativeLibLoader.loadLibraries();
351+ initIDs();
352+ }
353+
354+ private native boolean parseImage(int x, int y, int width, int height,
355+ boolean interlace, int initCodeSize,
356+ byte block[], byte rasline[],
357+ IndexColorModel model);
358+
359+ private int sendPixels(int x, int y, int width, int height,
360+ byte rasline[], ColorModel model) {
361+ int rasbeg, rasend, x2;
362+ if (y < 0) {
363+ height += y;
364+ y = 0;
365+ }
366+ if (y + height > global_height) {
367+ height = global_height - y;
368+ }
369+ if (height <= 0) {
370+ return 1;
371+ }
372+ // rasline[0] == pixel at coordinate (x,y)
373+ // rasline[width] == pixel at coordinate (x+width, y)
374+ if (x < 0) {
375+ rasbeg = -x;
376+ width += x; // same as (width -= rasbeg)
377+ x2 = 0; // same as (x2 = x + rasbeg)
378+ } else {
379+ rasbeg = 0;
380+ // width -= 0; // same as (width -= rasbeg)
381+ x2 = x; // same as (x2 = x + rasbeg)
382+ }
383+ // rasline[rasbeg] == pixel at coordinate (x2,y)
384+ // rasline[width] == pixel at coordinate (x+width, y)
385+ // rasline[rasbeg + width] == pixel at coordinate (x2+width, y)
386+ if (x2 + width > global_width) {
387+ width = global_width - x2;
388+ }
389+ if (width <= 0) {
390+ return 1;
391+ }
392+ rasend = rasbeg + width;
393+ // rasline[rasbeg] == pixel at coordinate (x2,y)
394+ // rasline[rasend] == pixel at coordinate (x2+width, y)
395+ int off = y * global_width + x2;
396+ boolean save = (curframe.disposal_method == GifFrame.DISPOSAL_SAVE);
397+ if (trans_pixel >= 0 && !curframe.initialframe) {
398+ if (saved_image != null && model.equals(saved_model)) {
399+ for (int i = rasbeg; i < rasend; i++, off++) {
400+ byte pixel = rasline[i];
401+ if ((pixel & 0xff) == trans_pixel) {
402+ rasline[i] = saved_image[off];
403+ } else if (save) {
404+ saved_image[off] = pixel;
405+ }
406+ }
407+ } else {
408+ // We have to do this the hard way - only transmit
409+ // the non-transparent sections of the line...
410+ // Fix for 6301050: the interlacing is ignored in this case
411+ // in order to avoid artefacts in case of animated images.
412+ int runstart = -1;
413+ int count = 1;
414+ for (int i = rasbeg; i < rasend; i++, off++) {
415+ byte pixel = rasline[i];
416+ if ((pixel & 0xff) == trans_pixel) {
417+ if (runstart >= 0) {
418+ count = setPixels(x + runstart, y,
419+ i - runstart, 1,
420+ model, rasline,
421+ runstart, 0);
422+ if (count == 0) {
423+ break;
424+ }
425+ }
426+ runstart = -1;
427+ } else {
428+ if (runstart < 0) {
429+ runstart = i;
430+ }
431+ if (save) {
432+ saved_image[off] = pixel;
433+ }
434+ }
435+ }
436+ if (runstart >= 0) {
437+ count = setPixels(x + runstart, y,
438+ rasend - runstart, 1,
439+ model, rasline,
440+ runstart, 0);
441+ }
442+ return count;
443+ }
444+ } else if (save) {
445+ System.arraycopy(rasline, rasbeg, saved_image, off, width);
446+ }
447+ int count = setPixels(x2, y, width, height, model,
448+ rasline, rasbeg, 0);
449+ return count;
450+ }
451+
452+ /**
453+ * Read Image data
454+ */
455+ private boolean readImage(boolean first, int disposal_method, int delay)
456+ throws IOException
457+ {
458+ if (curframe != null && !curframe.dispose()) {
459+ abort();
460+ return false;
461+ }
462+
463+ long tm = 0;
464+
465+ if (verbose) {
466+ tm = System.currentTimeMillis();
467+ }
468+
469+ // Allocate the buffer
470+ byte block[] = new byte[256 + 3];
471+
472+ // Read the image descriptor
473+ if (readBytes(block, 0, 10) != 0) {
474+ throw new IOException();
475+ }
476+ int x = ExtractWord(block, 0);
477+ int y = ExtractWord(block, 2);
478+ int width = ExtractWord(block, 4);
479+ int height = ExtractWord(block, 6);
480+
481+ /*
482+ * Majority of gif images have
483+ * same logical screen and frame dimensions.
484+ * Also, Photoshop and Mozilla seem to use the logical
485+ * screen dimension (from the global stream header)
486+ * if frame dimension is invalid.
487+ *
488+ * We use similar heuristic and trying to recover
489+ * frame width from logical screen dimension and
490+ * frame offset.
491+ */
492+ if (width == 0 && global_width != 0) {
493+ width = global_width - x;
494+ }
495+ if (height == 0 && global_height != 0) {
496+ height = global_height - y;
497+ }
498+
499+ boolean interlace = (block[8] & INTERLACEMASK) != 0;
500+
501+ IndexColorModel model = global_model;
502+
503+ if ((block[8] & COLORMAPMASK) != 0) {
504+ // We read one extra byte above so now when we must
505+ // transfer that byte as the first colormap byte
506+ // and manually read the code size when we are done
507+ int num_local_colors = 1 << ((block[8] & 0x7) + 1);
508+
509+ // Read local colors
510+ byte[] local_colormap = new byte[num_local_colors * 3];
511+ local_colormap[0] = block[9];
512+ if (readBytes(local_colormap, 1, num_local_colors * 3 - 1) != 0) {
513+ throw new IOException();
514+ }
515+
516+ // Now read the "real" code size byte which follows
517+ // the local color table
518+ if (readBytes(block, 9, 1) != 0) {
519+ throw new IOException();
520+ }
521+ if (trans_pixel >= num_local_colors) {
522+ // Fix for 4233748: extend colormap to contain transparent pixel
523+ num_local_colors = trans_pixel + 1;
524+ local_colormap = grow_colormap(local_colormap, num_local_colors);
525+ }
526+ model = new IndexColorModel(8, num_local_colors, local_colormap,
527+ 0, false, trans_pixel);
528+ } else if (model == null
529+ || trans_pixel != model.getTransparentPixel()) {
530+ if (trans_pixel >= num_global_colors) {
531+ // Fix for 4233748: extend colormap to contain transparent pixel
532+ num_global_colors = trans_pixel + 1;
533+ global_colormap = grow_colormap(global_colormap, num_global_colors);
534+ }
535+ model = new IndexColorModel(8, num_global_colors, global_colormap,
536+ 0, false, trans_pixel);
537+ global_model = model;
538+ }
539+
540+ // Notify the consumers
541+ if (first) {
542+ if (global_width == 0) global_width = width;
543+ if (global_height == 0) global_height = height;
544+
545+ setDimensions(global_width, global_height);
546+ setProperties(props);
547+ setColorModel(model);
548+ headerComplete();
549+ }
550+
551+ if (disposal_method == GifFrame.DISPOSAL_SAVE && saved_image == null) {
552+ saved_image = new byte[global_width * global_height];
553+ /*
554+ * If height of current image is smaller than the global height,
555+ * fill the gap with transparent pixels.
556+ */
557+ if ((height < global_height) && (model != null)) {
558+ byte tpix = (byte)model.getTransparentPixel();
559+ if (tpix >= 0) {
560+ byte trans_rasline[] = new byte[global_width];
561+ for (int i=0; i<global_width;i++) {
562+ trans_rasline[i] = tpix;
563+ }
564+
565+ setPixels(0, 0, global_width, y,
566+ model, trans_rasline, 0, 0);
567+ setPixels(0, y+height, global_width,
568+ global_height-height-y, model, trans_rasline,
569+ 0, 0);
570+ }
571+ }
572+ }
573+
574+ int hints = (interlace ? interlaceflags : normalflags);
575+ setHints(hints);
576+
577+ curframe = new GifFrame(this, disposal_method, delay,
578+ (curframe == null), model,
579+ x, y, width, height);
580+
581+ // allocate the raster data
582+ byte rasline[] = new byte[width];
583+
584+ if (verbose) {
585+ System.out.print("Reading a " + width + " by " + height + " " +
586+ (interlace ? "" : "non-") + "interlaced image...");
587+ }
588+ int initCodeSize = ExtractByte(block, 9);
589+ if (initCodeSize >= 12) {
590+ if (verbose) {
591+ System.out.println("Invalid initial code size: " +
592+ initCodeSize);
593+ }
594+ return false;
595+ }
596+ boolean ret = parseImage(x, y, width, height,
597+ interlace, initCodeSize,
598+ block, rasline, model);
599+
600+ if (!ret) {
601+ abort();
602+ }
603+
604+ if (verbose) {
605+ System.out.println("done in "
606+ + (System.currentTimeMillis() - tm)
607+ + "ms");
608+ }
609+
610+ return ret;
611+ }
612+
613+ public static byte[] grow_colormap(byte[] colormap, int newlen) {
614+ byte[] newcm = new byte[newlen * 3];
615+ System.arraycopy(colormap, 0, newcm, 0, colormap.length);
616+ return newcm;
617+ }
618+}
619+
620+class GifFrame {
621+ private static final boolean verbose = false;
622+ private static IndexColorModel trans_model;
623+
624+ static final int DISPOSAL_NONE = 0x00;
625+ static final int DISPOSAL_SAVE = 0x01;
626+ static final int DISPOSAL_BGCOLOR = 0x02;
627+ static final int DISPOSAL_PREVIOUS = 0x03;
628+
629+ GifImageDecoder decoder;
630+
631+ int disposal_method;
632+ int delay;
633+
634+ IndexColorModel model;
635+
636+ int x;
637+ int y;
638+ int width;
639+ int height;
640+
641+ boolean initialframe;
642+
643+ public GifFrame(GifImageDecoder id, int dm, int dl, boolean init,
644+ IndexColorModel cm, int x, int y, int w, int h) {
645+ this.decoder = id;
646+ this.disposal_method = dm;
647+ this.delay = dl;
648+ this.model = cm;
649+ this.initialframe = init;
650+ this.x = x;
651+ this.y = y;
652+ this.width = w;
653+ this.height = h;
654+ }
655+
656+ private void setPixels(int x, int y, int w, int h,
657+ ColorModel cm, byte[] pix, int off, int scan) {
658+ decoder.setPixels(x, y, w, h, cm, pix, off, scan);
659+ }
660+
661+ public boolean dispose() {
662+ if (decoder.imageComplete(ImageConsumer.SINGLEFRAMEDONE, false) == 0) {
663+ return false;
664+ } else {
665+ if (delay > 0) {
666+ try {
667+ if (verbose) {
668+ System.out.println("sleeping: "+delay);
669+ }
670+ Thread.sleep(delay);
671+ } catch (InterruptedException e) {
672+ return false;
673+ }
674+ } else {
675+ Thread.yield();
676+ }
677+
678+ if (verbose && disposal_method != 0) {
679+ System.out.println("disposal method: "+disposal_method);
680+ }
681+
682+ int global_width = decoder.global_width;
683+ int global_height = decoder.global_height;
684+
685+ if (x < 0) {
686+ width += x;
687+ x = 0;
688+ }
689+ if (x + width > global_width) {
690+ width = global_width - x;
691+ }
692+ if (width <= 0) {
693+ disposal_method = DISPOSAL_NONE;
694+ } else {
695+ if (y < 0) {
696+ height += y;
697+ y = 0;
698+ }
699+ if (y + height > global_height) {
700+ height = global_height - y;
701+ }
702+ if (height <= 0) {
703+ disposal_method = DISPOSAL_NONE;
704+ }
705+ }
706+
707+ switch (disposal_method) {
708+ case DISPOSAL_PREVIOUS:
709+ byte[] saved_image = decoder.saved_image;
710+ IndexColorModel saved_model = decoder.saved_model;
711+ if (saved_image != null) {
712+ setPixels(x, y, width, height,
713+ saved_model, saved_image,
714+ y * global_width + x, global_width);
715+ }
716+ break;
717+ case DISPOSAL_BGCOLOR:
718+ byte tpix;
719+ if (model.getTransparentPixel() < 0) {
720+ model = trans_model;
721+ if (model == null) {
722+ model = new IndexColorModel(8, 1,
723+ new byte[4], 0, true);
724+ trans_model = model;
725+ }
726+ tpix = 0;
727+ } else {
728+ tpix = (byte) model.getTransparentPixel();
729+ }
730+ byte[] rasline = new byte[width];
731+ if (tpix != 0) {
732+ for (int i = 0; i < width; i++) {
733+ rasline[i] = tpix;
734+ }
735+ }
736+
737+ // clear saved_image using transparent pixels
738+ // this will be used as the background in the next display
739+ if( decoder.saved_image != null ) {
740+ for( int i = 0; i < global_width * global_height; i ++ )
741+ decoder.saved_image[i] = tpix;
742+ }
743+
744+ setPixels(x, y, width, height, model, rasline, 0, 0);
745+ break;
746+ case DISPOSAL_SAVE:
747+ decoder.saved_model = model;
748+ break;
749+ }
750+ }
751+ return true;
752+ }
753+}